@ng-org/alien-deepsignals 0.1.2-alpha.2 → 0.1.2-alpha.4

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 (63) hide show
  1. package/dist/deepSignal.d.ts.map +1 -1
  2. package/dist/deepSignal.js +244 -100
  3. package/dist/hooks/react/useDeepSignal.d.ts +4 -4
  4. package/dist/hooks/react/useDeepSignal.d.ts.map +1 -1
  5. package/dist/hooks/react/useDeepSignal.js +22 -15
  6. package/dist/hooks/svelte/useDeepSignal.svelte.d.ts +3 -7
  7. package/dist/hooks/svelte/useDeepSignal.svelte.d.ts.map +1 -1
  8. package/dist/hooks/svelte/useDeepSignal.svelte.js +34 -18
  9. package/dist/hooks/vue/useDeepSignal.d.ts +4 -3
  10. package/dist/hooks/vue/useDeepSignal.d.ts.map +1 -1
  11. package/dist/hooks/vue/useDeepSignal.js +52 -24
  12. package/dist/test/frontend/astro-app/src/components/PerfSuiteClient.d.ts +4 -0
  13. package/dist/test/frontend/astro-app/src/components/PerfSuiteClient.d.ts.map +1 -0
  14. package/dist/test/frontend/astro-app/src/components/PerfSuiteClient.js +225 -0
  15. package/dist/test/frontend/astro-app/src/components/ReactPanel.d.ts +4 -0
  16. package/dist/test/frontend/astro-app/src/components/ReactPanel.d.ts.map +1 -0
  17. package/dist/test/frontend/astro-app/src/components/ReactPanel.js +227 -0
  18. package/dist/test/frontend/astro-app/src/components/perf/react/ReactPerfDeep.d.ts +4 -0
  19. package/dist/test/frontend/astro-app/src/components/perf/react/ReactPerfDeep.d.ts.map +1 -0
  20. package/dist/test/frontend/astro-app/src/components/perf/react/ReactPerfDeep.js +150 -0
  21. package/dist/test/frontend/astro-app/src/components/perf/react/ReactPerfNative.d.ts +4 -0
  22. package/dist/test/frontend/astro-app/src/components/perf/react/ReactPerfNative.d.ts.map +1 -0
  23. package/dist/test/frontend/astro-app/src/components/perf/react/ReactPerfNative.js +184 -0
  24. package/dist/test/frontend/playwright/crossFrameworkHooks.spec.d.ts +2 -0
  25. package/dist/test/frontend/playwright/crossFrameworkHooks.spec.d.ts.map +1 -0
  26. package/dist/test/frontend/playwright/crossFrameworkHooks.spec.js +171 -0
  27. package/dist/test/frontend/playwright/perfSuite.spec.d.ts +2 -0
  28. package/dist/test/frontend/playwright/perfSuite.spec.d.ts.map +1 -0
  29. package/dist/test/frontend/playwright/perfSuite.spec.js +128 -0
  30. package/dist/test/frontend/utils/mockData.d.ts +53 -0
  31. package/dist/test/frontend/utils/mockData.d.ts.map +1 -0
  32. package/dist/test/frontend/utils/mockData.js +78 -0
  33. package/dist/test/frontend/utils/paths.d.ts +4 -0
  34. package/dist/test/frontend/utils/paths.d.ts.map +1 -0
  35. package/dist/test/frontend/utils/paths.js +28 -0
  36. package/dist/test/frontend/utils/perfScenarios.d.ts +15 -0
  37. package/dist/test/frontend/utils/perfScenarios.d.ts.map +1 -0
  38. package/dist/test/frontend/utils/perfScenarios.js +287 -0
  39. package/dist/test/frontend/utils/renderMetrics.d.ts +13 -0
  40. package/dist/test/frontend/utils/renderMetrics.d.ts.map +1 -0
  41. package/dist/test/frontend/utils/renderMetrics.js +45 -0
  42. package/dist/test/frontend/utils/state.d.ts +57 -0
  43. package/dist/test/frontend/utils/state.d.ts.map +1 -0
  44. package/dist/test/frontend/utils/state.js +79 -0
  45. package/dist/test/lib/core.test.d.ts +2 -0
  46. package/dist/test/lib/core.test.d.ts.map +1 -0
  47. package/dist/test/lib/core.test.js +53 -0
  48. package/dist/test/lib/deepSignalOptions.test.d.ts +2 -0
  49. package/dist/test/lib/deepSignalOptions.test.d.ts.map +1 -0
  50. package/dist/test/lib/deepSignalOptions.test.js +230 -0
  51. package/dist/test/lib/index.test.d.ts +2 -0
  52. package/dist/test/lib/index.test.d.ts.map +1 -0
  53. package/dist/test/lib/index.test.js +807 -0
  54. package/dist/test/lib/misc.test.d.ts +2 -0
  55. package/dist/test/lib/misc.test.d.ts.map +1 -0
  56. package/dist/test/lib/misc.test.js +140 -0
  57. package/dist/test/lib/watch.test.d.ts +2 -0
  58. package/dist/test/lib/watch.test.d.ts.map +1 -0
  59. package/dist/test/lib/watch.test.js +1280 -0
  60. package/dist/types.d.ts +1 -1
  61. package/dist/types.d.ts.map +1 -1
  62. package/package.json +36 -19
  63. package/src/index.ts +5 -0
@@ -11,7 +11,7 @@
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  const watch_js_1 = require("../../watch.js");
13
13
  const react_1 = require("react");
14
- const deepSignal_1 = require("../../deepSignal");
14
+ const __1 = require("../..");
15
15
  /**
16
16
  * Create or use an existing deepSignal object in your component.
17
17
  * Modifications to the returned deepSignal object cause an immediate rerender.
@@ -19,21 +19,28 @@ const deepSignal_1 = require("../../deepSignal");
19
19
  * is rerendered as well.
20
20
  *
21
21
  * @param object The object that should become reactive
22
- * @param deepSignalObjects When the object is not a deepSignal already, options passed to `deepSignal`.
23
- * @returns The deepSignal object of the object param.
22
+ * @param deepSignalOptions When the object is not a deepSignal already, options passed to `deepSignal`.
23
+ * @returns The deepSignal object of the object param. On every change, the returned object will change (a new no-op proxy is created) around the deepSignal object.
24
24
  */
25
- const useSignal = (object, deepSignalObjects) => {
26
- const shapeSignalRef = (0, react_1.useRef)((0, deepSignal_1.deepSignal)(object, deepSignalObjects));
27
- const [, setTick] = (0, react_1.useState)(0);
28
- (0, react_1.useEffect)(() => {
29
- const { stopListening } = (0, watch_js_1.watch)(shapeSignalRef.current, () => {
30
- // trigger a React re-render when the deep signal updates
31
- setTick((t) => t + 1);
25
+ const useSignal = (object, deepSignalOptions) => {
26
+ // Create the actual deepSignal object from the raw object (if the object is a deepSignal object already, it returns itself).
27
+ const signal = (0, react_1.useMemo)(() => (0, __1.deepSignal)(object, deepSignalOptions), [object, deepSignalOptions]);
28
+ // Create a shallow proxy of the original object which can be disposed and a new one
29
+ // recreated on rerenders so that react knows it changed on comparisons.
30
+ const proxyRef = (0, react_1.useRef)(new Proxy(signal, {}));
31
+ // Update proxy ref when shapeSignal changes
32
+ (0, react_1.useMemo)(() => {
33
+ proxyRef.current = new Proxy(signal, {});
34
+ }, [signal]);
35
+ const subscribe = (0, react_1.useCallback)((onStoreChange) => {
36
+ const { stopListening } = (0, watch_js_1.watch)(signal, () => {
37
+ // Create a new shallow proxy and notify react about the change.
38
+ proxyRef.current = new Proxy(signal, {});
39
+ onStoreChange();
32
40
  }, { triggerInstantly: true });
33
- return () => {
34
- stopListening();
35
- };
36
- }, []);
37
- return shapeSignalRef.current;
41
+ return stopListening;
42
+ }, [signal]);
43
+ const getSnapshot = (0, react_1.useCallback)(() => proxyRef.current, []);
44
+ return (0, react_1.useSyncExternalStore)(subscribe, getSnapshot);
38
45
  };
39
46
  exports.default = useSignal;
@@ -1,11 +1,7 @@
1
1
  import { type Readable } from "svelte/store";
2
- import { type DeepPatchBatch, DeepSignalOptions, RevertDeepSignal } from "../../index";
2
+ import { DeepSignalOptions, RevertDeepSignal } from "../../index";
3
3
  /** Base result contract for a deepSignal-backed Svelte integration. */
4
- export interface UseDeepSignalResult<T extends object> extends Readable<T> {
5
- /** Store of the full deep proxy tree (also accessible via `subscribe` directly on this result). */
6
- deep: Readable<T>;
7
- /** Last batch of deep mutation patches for this root (empties only on next non-empty batch). */
8
- patches: Readable<DeepPatchBatch | null>;
4
+ export interface UseDeepSignalResult<T> extends Readable<T> {
9
5
  /** Derive a nested selection; re-runs when the underlying tree version increments. */
10
6
  select<U>(selector: (tree: T) => U): Readable<U>;
11
7
  /** Stop receiving further updates (invoked automatically on component destroy). */
@@ -26,6 +22,6 @@ export interface UseDeepSignalResult<T extends object> extends Readable<T> {
26
22
  * @param deepSignalObjects When the object is not a deepSignal already, options passed to `deepSignal`.
27
23
  * @returns A rune for using the deepSignal object in svelte.
28
24
  */
29
- export declare function useDeepSignal<T extends object>(object: T, options?: DeepSignalOptions): UseDeepSignalResult<RevertDeepSignal<T>>;
25
+ export declare function useDeepSignal<T extends object>(object: T | Promise<T>, options?: DeepSignalOptions): UseDeepSignalResult<RevertDeepSignal<T>>;
30
26
  export default useDeepSignal;
31
27
  //# sourceMappingURL=useDeepSignal.svelte.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useDeepSignal.svelte.d.ts","sourceRoot":"","sources":["../../../src/hooks/svelte/useDeepSignal.svelte.ts"],"names":[],"mappings":"AAUA,OAAO,EAAqB,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAEhE,OAAO,EAGH,KAAK,cAAc,EACnB,iBAAiB,EAEjB,gBAAgB,EAEnB,MAAM,aAAa,CAAC;AAErB,uEAAuE;AACvE,MAAM,WAAW,mBAAmB,CAAC,CAAC,SAAS,MAAM,CAAE,SAAQ,QAAQ,CAAC,CAAC,CAAC;IACtE,mGAAmG;IACnG,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;IAClB,gGAAgG;IAChG,OAAO,EAAE,QAAQ,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IACzC,sFAAsF;IACtF,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IACjD,mFAAmF;IACnF,OAAO,IAAI,IAAI,CAAC;IAChB,sGAAsG;IACtG,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;IAChC,4DAA4D;IAC5D,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;CACnD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,EAC1C,MAAM,EAAE,CAAC,EACT,OAAO,CAAC,EAAE,iBAAiB,GAC5B,mBAAmB,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAiD1C;AAED,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"useDeepSignal.svelte.d.ts","sourceRoot":"","sources":["../../../src/hooks/svelte/useDeepSignal.svelte.ts"],"names":[],"mappings":"AAUA,OAAO,EAAqB,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAEhE,OAAO,EAIH,iBAAiB,EAEjB,gBAAgB,EAEnB,MAAM,aAAa,CAAC;AAErB,uEAAuE;AACvE,MAAM,WAAW,mBAAmB,CAAC,CAAC,CAAE,SAAQ,QAAQ,CAAC,CAAC,CAAC;IACvD,sFAAsF;IACtF,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IACjD,mFAAmF;IACnF,OAAO,IAAI,IAAI,CAAC;IAChB,sGAAsG;IACtG,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;IAChC,4DAA4D;IAC5D,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;CACnD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,EAC1C,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,EACtB,OAAO,CAAC,EAAE,iBAAiB,GAC5B,mBAAmB,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAkE1C;AAED,eAAe,aAAa,CAAC"}
@@ -25,26 +25,42 @@ const index_1 = require("../../index");
25
25
  * @returns A rune for using the deepSignal object in svelte.
26
26
  */
27
27
  function useDeepSignal(object, options) {
28
- const deepProxy = (0, index_1.deepSignal)(object, options);
29
- const rootId = (0, index_1.getDeepSignalRootId)(deepProxy);
30
- const initialVersion = (0, index_1.getDeepSignalVersion)(deepProxy) ?? 0;
31
- const version = (0, store_1.writable)(initialVersion);
32
- const patchesStore = (0, store_1.writable)(null);
33
- const unsubscribe = (0, index_1.subscribeDeepMutations)(deepProxy, (batch) => {
34
- if (!rootId)
28
+ const version = (0, store_1.writable)(-1);
29
+ let deepProxy;
30
+ let unsubscribe;
31
+ let isDestroyed = false;
32
+ const init = (obj) => {
33
+ if (isDestroyed)
35
34
  return;
36
- if (batch.patches.length) {
37
- patchesStore.set(batch);
38
- version.set(batch.version);
39
- }
40
- });
41
- const deep = (0, store_1.derived)(version, () => deepProxy);
42
- const select = (selector) => (0, store_1.derived)(deep, (t) => selector(t));
43
- const dispose = () => unsubscribe();
35
+ deepProxy = (0, index_1.deepSignal)(obj, options);
36
+ const rootId = (0, index_1.getDeepSignalRootId)(deepProxy);
37
+ const initialVersion = (0, index_1.getDeepSignalVersion)(deepProxy) ?? 0;
38
+ unsubscribe = (0, index_1.subscribeDeepMutations)(deepProxy, (batch) => {
39
+ if (!rootId)
40
+ return;
41
+ if (batch.patches.length) {
42
+ version.set(batch.version);
43
+ }
44
+ });
45
+ version.set(initialVersion);
46
+ };
47
+ if (object instanceof Promise) {
48
+ object.then(init);
49
+ }
50
+ else {
51
+ init(object);
52
+ }
53
+ const dispose = () => {
54
+ isDestroyed = true;
55
+ if (unsubscribe)
56
+ unsubscribe();
57
+ };
44
58
  (0, svelte_1.onDestroy)(dispose);
59
+ const deep = (0, store_1.derived)(version, () => deepProxy);
60
+ const select = (selector) => (0, store_1.derived)(deep, (t) => (t ? selector(t) : undefined));
45
61
  // Expose Svelte store contract by delegating subscribe to deep store.
46
62
  const applyReplacement = (next) => {
47
- if (!next || typeof next !== "object")
63
+ if (!deepProxy || !next || typeof next !== "object")
48
64
  return;
49
65
  // Remove keys absent in next
50
66
  for (const k of Object.keys(deepProxy)) {
@@ -55,8 +71,6 @@ function useDeepSignal(object, options) {
55
71
  Object.assign(deepProxy, next);
56
72
  };
57
73
  const store = {
58
- deep,
59
- patches: patchesStore,
60
74
  select,
61
75
  dispose,
62
76
  subscribe: deep.subscribe,
@@ -64,6 +78,8 @@ function useDeepSignal(object, options) {
64
78
  applyReplacement(next);
65
79
  },
66
80
  update(updater) {
81
+ if (!deepProxy)
82
+ return;
67
83
  const result = updater(deepProxy);
68
84
  if (result && typeof result === "object")
69
85
  applyReplacement(result);
@@ -1,3 +1,4 @@
1
+ import { type MaybeRefOrGetter } from "vue";
1
2
  import { DeepSignal, DeepSignalOptions } from "../../";
2
3
  /**
3
4
  * Create or use an existing deepSignal object in your component.
@@ -5,11 +6,11 @@ import { DeepSignal, DeepSignalOptions } from "../../";
5
6
  * If modifications of the object are made from somewhere else, the component
6
7
  * is rerendered as well.
7
8
  *
8
- * @param object The object that should become reactive
9
- * @param deepSignalObjects When the object is not a deepSignal already, options passed to `deepSignal`.
9
+ * @param object The object that should become reactive (can be a ref or getter)
10
+ * @param options When the object is not a deepSignal already, options passed to `deepSignal`.
10
11
  * @returns The deepSignal object of the object param.
11
12
  *
12
13
  */
13
- export declare function useDeepSignal<T extends object>(object: T, options?: DeepSignalOptions): DeepSignal<T>;
14
+ export declare function useDeepSignal<T extends object>(object: MaybeRefOrGetter<T>, options?: DeepSignalOptions): DeepSignal<T>;
14
15
  export default useDeepSignal;
15
16
  //# sourceMappingURL=useDeepSignal.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useDeepSignal.d.ts","sourceRoot":"","sources":["../../../src/hooks/vue/useDeepSignal.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,UAAU,EAAc,iBAAiB,EAAS,MAAM,QAAQ,CAAC;AAE1E;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,EAC1C,MAAM,EAAE,CAAC,EACT,OAAO,CAAC,EAAE,iBAAiB,GAC5B,UAAU,CAAC,CAAC,CAAC,CAgDf;AAED,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"useDeepSignal.d.ts","sourceRoot":"","sources":["../../../src/hooks/vue/useDeepSignal.ts"],"names":[],"mappings":"AAUA,OAAO,EAGH,KAAK,gBAAgB,EAGxB,MAAM,KAAK,CAAC;AACb,OAAO,EAAE,UAAU,EAAc,iBAAiB,EAAS,MAAM,QAAQ,CAAC;AAE1E;;;;;;;;;;GAUG;AAEH,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,EAC1C,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAC3B,OAAO,CAAC,EAAE,iBAAiB,GAC5B,UAAU,CAAC,CAAC,CAAC,CAkFf;AAED,eAAe,aAAa,CAAC"}
@@ -18,54 +18,82 @@ const __1 = require("../../");
18
18
  * If modifications of the object are made from somewhere else, the component
19
19
  * is rerendered as well.
20
20
  *
21
- * @param object The object that should become reactive
22
- * @param deepSignalObjects When the object is not a deepSignal already, options passed to `deepSignal`.
21
+ * @param object The object that should become reactive (can be a ref or getter)
22
+ * @param options When the object is not a deepSignal already, options passed to `deepSignal`.
23
23
  * @returns The deepSignal object of the object param.
24
24
  *
25
25
  */
26
+ // Note partly written with the help of Gemini 3.
26
27
  function useDeepSignal(object, options) {
27
28
  const version = (0, vue_1.ref)(0);
28
- const signalObject = (0, __1.deepSignal)(object, options);
29
- const stopHandle = (0, __1.watch)(signalObject, ({ patches }) => {
30
- if (patches.length > 0) {
31
- version.value++;
29
+ // Holds the current reactive signal object.
30
+ let currentSignal;
31
+ let stopHandle;
32
+ // Watch the input object for changes.
33
+ const stopWatchingSource = (0, vue_1.watch)(() => (0, vue_1.toValue)(object), (newObj) => {
34
+ if (stopHandle) {
35
+ stopHandle.stopListening();
36
+ stopHandle = undefined;
32
37
  }
33
- });
34
- // Proxy that creates Vue dependency on version for any access
35
- const proxy = new Proxy(signalObject, {
38
+ if (newObj) {
39
+ currentSignal = (0, __1.deepSignal)(newObj, options);
40
+ stopHandle = (0, __1.watch)(currentSignal, ({ patches }) => {
41
+ if (patches.length > 0)
42
+ version.value++;
43
+ });
44
+ }
45
+ // Trigger Vue update.
46
+ version.value++;
47
+ }, { immediate: true });
48
+ // Determines the initial target for the Proxy (array vs object).
49
+ const initialVal = (0, vue_1.toValue)(object);
50
+ const proxyTarget = (Array.isArray(initialVal) ? [] : {});
51
+ // Proxy that creates Vue dependency on version for any access.
52
+ const proxy = new Proxy(proxyTarget, {
36
53
  get(target, key) {
37
54
  if (key === "__raw")
38
- return target;
39
- // Track version to create reactive dependency
55
+ return currentSignal;
56
+ // Track version to create reactive dependency.
40
57
  version.value;
41
- const value = target[key];
42
- // Bind methods to maintain correct `this` context
43
- return typeof value === "function" ? value.bind(target) : value;
58
+ // Delegate to current signal.
59
+ const actualTarget = currentSignal || target;
60
+ const value = Reflect.get(actualTarget, key);
61
+ // Bind methods to maintain correct `this` context.
62
+ return typeof value === "function"
63
+ ? value.bind(actualTarget)
64
+ : value;
44
65
  },
45
66
  set(target, key, value) {
46
- // Directly forward writes to the deep signal root so other frameworks observe the change.
47
- return Reflect.set(target, key, value);
67
+ // Delegate to current signal.
68
+ const actualTarget = currentSignal || target;
69
+ return Reflect.set(actualTarget, key, value);
48
70
  },
49
71
  has(target, key) {
50
72
  version.value;
51
- return key in target;
73
+ const actualTarget = currentSignal || target;
74
+ return Reflect.has(actualTarget, key);
52
75
  },
53
76
  ownKeys(target) {
54
77
  version.value;
55
- return Reflect.ownKeys(target);
78
+ const actualTarget = currentSignal || target;
79
+ return Reflect.ownKeys(actualTarget);
56
80
  },
57
81
  getOwnPropertyDescriptor(target, key) {
58
82
  version.value;
59
- const desc = Object.getOwnPropertyDescriptor(target, key);
83
+ const actualTarget = currentSignal || target;
84
+ const desc = Reflect.getOwnPropertyDescriptor(actualTarget, key);
60
85
  return desc ? { ...desc, configurable: true } : undefined;
61
86
  },
62
87
  });
63
88
  (0, vue_1.onBeforeUnmount)(() => {
64
- try {
65
- stopHandle.stopListening();
66
- }
67
- catch {
68
- // ignore
89
+ stopWatchingSource();
90
+ if (stopHandle) {
91
+ try {
92
+ stopHandle.stopListening();
93
+ }
94
+ catch {
95
+ // ignore
96
+ }
69
97
  }
70
98
  });
71
99
  return proxy;
@@ -0,0 +1,4 @@
1
+ import React from "react";
2
+ declare const PerfSuiteClient: React.FC;
3
+ export default PerfSuiteClient;
4
+ //# sourceMappingURL=PerfSuiteClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PerfSuiteClient.d.ts","sourceRoot":"","sources":["../../../../../../src/test/frontend/astro-app/src/components/PerfSuiteClient.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAuC,MAAM,OAAO,CAAC;AA+C5D,QAAA,MAAM,eAAe,EAAE,KAAK,CAAC,EAkL5B,CAAC;AAEF,eAAe,eAAe,CAAC"}
@@ -0,0 +1,225 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const react_1 = __importStar(require("react"));
37
+ const perfScenarios_1 = require("../../../utils/perfScenarios");
38
+ const MAX_RESULTS = 12;
39
+ const sortResults = (results) => [...results].sort((a, b) => (b.completedAt ?? 0) - (a.completedAt ?? 0));
40
+ const resultSignature = (result) => [
41
+ result.framework,
42
+ result.variant,
43
+ result.completedAt ?? result.totalDuration,
44
+ result.runCount,
45
+ ].join(":");
46
+ const attachEntryId = (result) => ({
47
+ ...result,
48
+ entryId: `${result.framework}-${result.variant}-${result.completedAt ?? Date.now()}-${Math.random()
49
+ .toString(36)
50
+ .slice(2, 8)}`,
51
+ });
52
+ const seedResults = () => {
53
+ if (typeof window === "undefined")
54
+ return [];
55
+ const latest = window.perfSuite?.latestResults;
56
+ if (!latest)
57
+ return [];
58
+ const flattened = Object.values(latest).flatMap((variants) => Object.values(variants));
59
+ return sortResults(flattened.map(attachEntryId));
60
+ };
61
+ const formatDuration = (value) => `${value.toFixed(2)}ms`;
62
+ const formatSubRenderCount = (block, framework) => {
63
+ const entries = block.objectRenderCounts?.[framework];
64
+ if (!entries)
65
+ return 0;
66
+ return Object.values(entries).reduce((sum, count) => sum + count, 0);
67
+ };
68
+ const PerfSuiteClient = () => {
69
+ const [results, setResults] = (0, react_1.useState)(seedResults);
70
+ (0, react_1.useEffect)(() => {
71
+ if ((0, perfScenarios_1.registerPerfRunners)()) {
72
+ return;
73
+ }
74
+ const timer = window.setInterval(() => {
75
+ if ((0, perfScenarios_1.registerPerfRunners)()) {
76
+ window.clearInterval(timer);
77
+ }
78
+ }, 50);
79
+ return () => {
80
+ window.clearInterval(timer);
81
+ };
82
+ }, []);
83
+ (0, react_1.useEffect)(() => {
84
+ if (typeof window === "undefined")
85
+ return;
86
+ const suite = window.perfSuite;
87
+ if (!suite?.subscribe)
88
+ return;
89
+ const unsubscribe = suite.subscribe((result) => {
90
+ setResults((current) => {
91
+ if (current.some((entry) => resultSignature(entry) === resultSignature(result))) {
92
+ return current;
93
+ }
94
+ const augmented = attachEntryId(result);
95
+ const next = sortResults([augmented, ...current]);
96
+ return next.slice(0, MAX_RESULTS);
97
+ });
98
+ });
99
+ return unsubscribe;
100
+ }, []);
101
+ const renderedList = (0, react_1.useMemo)(() => {
102
+ if (!results.length) {
103
+ return (<p className="perf-suite-results__empty">
104
+ Awaiting perf runner output…
105
+ </p>);
106
+ }
107
+ return results.map((result) => (<li key={result.entryId} className="perf-suite-results__item">
108
+ <header>
109
+ <strong>
110
+ {result.framework}/{result.variant}
111
+ </strong>
112
+ <span>{formatDuration(result.totalDuration)}</span>
113
+ </header>
114
+ <div className="perf-suite-results__meta">
115
+ <span>
116
+ runs {result.runCount}
117
+ {result.warmupCount
118
+ ? ` (+${result.warmupCount} warmup)`
119
+ : ""}
120
+ </span>
121
+ {result.completedAt ? (<time>
122
+ {new Date(result.completedAt).toLocaleTimeString()}
123
+ </time>) : null}
124
+ </div>
125
+ <dl>
126
+ {Object.entries(result.blocks).map(([name, block]) => (<react_1.default.Fragment key={name}>
127
+ <div className="perf-suite-results__block">
128
+ <dt>{name}</dt>
129
+ <dd>{formatDuration(block.duration)}</dd>
130
+ </div>
131
+ <div className="perf-suite-results__block perf-suite-results__block--subrenders">
132
+ <dt>Subcomponent renders</dt>
133
+ <dd>
134
+ {formatSubRenderCount(block, result.framework)}
135
+ </dd>
136
+ </div>
137
+ </react_1.default.Fragment>))}
138
+ </dl>
139
+ </li>));
140
+ }, [results]);
141
+ return (<div className="perf-suite-results">
142
+ <div className="perf-suite-results__header">
143
+ <h3>Latest Perf Runs</h3>
144
+ <p>Playwright tests stream into this dashboard.</p>
145
+ </div>
146
+ <ul>{renderedList}</ul>
147
+ <style>
148
+ {`
149
+ .perf-suite-results {
150
+ border: 1px solid var(--astro-color-border, rgba(120, 120, 120, 0.4));
151
+ border-radius: 0.75rem;
152
+ padding: 1rem;
153
+ margin-top: 1.5rem;
154
+ max-width: 720px;
155
+ }
156
+ .perf-suite-results__header {
157
+ display: flex;
158
+ flex-direction: column;
159
+ gap: 0.25rem;
160
+ margin-bottom: 0.5rem;
161
+ }
162
+ .perf-suite-results__header h3 {
163
+ margin: 0;
164
+ font-size: 1.1rem;
165
+ }
166
+ .perf-suite-results__header p {
167
+ margin: 0;
168
+ opacity: 0.8;
169
+ font-size: 0.9rem;
170
+ }
171
+ .perf-suite-results ul {
172
+ list-style: none;
173
+ padding: 0;
174
+ margin: 0;
175
+ display: flex;
176
+ flex-direction: column;
177
+ gap: 0.75rem;
178
+ }
179
+ .perf-suite-results__item {
180
+ background: rgba(120, 120, 120, 0.08);
181
+ border: 1px solid rgba(120, 120, 120, 0.3);
182
+ border-radius: 0.5rem;
183
+ padding: 0.75rem;
184
+ display: flex;
185
+ flex-direction: column;
186
+ gap: 0.35rem;
187
+ }
188
+ .perf-suite-results__item header {
189
+ display: flex;
190
+ justify-content: space-between;
191
+ font-size: 0.95rem;
192
+ }
193
+ .perf-suite-results__item header strong {
194
+ text-transform: capitalize;
195
+ }
196
+ .perf-suite-results__meta {
197
+ display: flex;
198
+ justify-content: space-between;
199
+ font-size: 0.8rem;
200
+ opacity: 0.8;
201
+ }
202
+ .perf-suite-results__block {
203
+ display: flex;
204
+ justify-content: space-between;
205
+ font-size: 0.85rem;
206
+ }
207
+ .perf-suite-results__block--subrenders {
208
+ opacity: 0.85;
209
+ }
210
+ .perf-suite-results__block dt {
211
+ margin: 0;
212
+ text-transform: capitalize;
213
+ }
214
+ .perf-suite-results__block dd {
215
+ margin: 0;
216
+ }
217
+ .perf-suite-results__empty {
218
+ font-style: italic;
219
+ opacity: 0.8;
220
+ }
221
+ `}
222
+ </style>
223
+ </div>);
224
+ };
225
+ exports.default = PerfSuiteClient;
@@ -0,0 +1,4 @@
1
+ import React from "react";
2
+ declare const ReactPanel: React.FC;
3
+ export default ReactPanel;
4
+ //# sourceMappingURL=ReactPanel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReactPanel.d.ts","sourceRoot":"","sources":["../../../../../../src/test/frontend/astro-app/src/components/ReactPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4B,MAAM,OAAO,CAAC;AAMjD,QAAA,MAAM,UAAU,EAAE,KAAK,CAAC,EAqSvB,CAAC;AAEF,eAAe,UAAU,CAAC"}