@deffector/agent 0.1.1 → 0.1.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.
package/README.md CHANGED
@@ -8,6 +8,18 @@ The panel is the deffector browser extension (Chrome, Edge, Firefox) or, outside
8
8
  npm install @deffector/agent
9
9
  ```
10
10
 
11
+ ## Requirements
12
+
13
+ - **effector** 22.6+ (tested with 23.4.4 and 22.8.6)
14
+ - **React** 16.8+ with effector-react 22+, **Vue** 3.3+ with effector-vue 23+, **Solid** 1.8+ with effector-solid 0.23+ — only for the framework you use
15
+ - **Next.js** 13.4+ (App Router, `@effector/next`), tested with 15.5
16
+ - **Browser** with the extension: Chrome or Edge 116+, Firefox 140+
17
+ - **Node** 18+ for the standalone app; 22+ for the agent in a Node process (older: pass `WebSocket` from `ws`)
18
+ - **TypeScript** 5.0+ with `moduleResolution` `"bundler"`, `"node16"` or `"nodenext"`
19
+ - **Bundler** that reads the `exports` field (Vite, webpack 5, Next.js, esbuild, Rollup); Parcel 2 needs `"@parcel/resolver-default": { "packageExports": true }`
20
+
21
+ Details: [Installation → Requirements](https://mxmvshnvsk.github.io/deffector/guide/installation#requirements).
22
+
11
23
  ## Use it
12
24
 
13
25
  Import the agent first, before any module that creates effector units:
@@ -21,7 +33,7 @@ Render the app through a provider deffector can switch (time travel and replay r
21
33
 
22
34
  ```tsx
23
35
  // React
24
- import { DeffectorProvider } from '@deffector/agent/react';
36
+ import { DeffectorProvider } from '@deffector/agent/react'; // hooks from effector-react/scope: '@deffector/agent/react-scope'
25
37
  <DeffectorProvider value={scope} name="client">
26
38
  <App />
27
39
  </DeffectorProvider>;
@@ -2031,7 +2031,13 @@ function describeDomNode(node) {
2031
2031
  }
2032
2032
 
2033
2033
  // ../protocol/src/encoding/encode.ts
2034
- var DEFAULT_LIMITS = { maxDepth: 8, maxKeys: 100, maxItems: 100, maxString: 2e3 };
2034
+ var DEFAULT_LIMITS = {
2035
+ maxDepth: 8,
2036
+ maxKeys: 100,
2037
+ maxItems: 100,
2038
+ maxString: 2e3,
2039
+ maxNodes: 2e3
2040
+ };
2035
2041
  var STACK_LINES = 6;
2036
2042
  function encodeString(text, maxString) {
2037
2043
  if (text.length <= maxString) return text;
@@ -2102,9 +2108,16 @@ var Encoder = class {
2102
2108
  constructor(options) {
2103
2109
  __publicField(this, "seen", /* @__PURE__ */ new WeakSet());
2104
2110
  __publicField(this, "options");
2111
+ /** values that may still be encoded in full (`maxNodes`) */
2112
+ __publicField(this, "budget");
2105
2113
  this.options = options;
2114
+ this.budget = options.maxNodes;
2115
+ }
2116
+ get exhausted() {
2117
+ return this.budget <= 0;
2106
2118
  }
2107
2119
  value(value, depth, path) {
2120
+ this.budget -= 1;
2108
2121
  if (value === null) return null;
2109
2122
  if (typeof value === "object") return this.object(value, depth, path);
2110
2123
  return encodeScalar(value, this.options.maxString);
@@ -2113,7 +2126,7 @@ var Encoder = class {
2113
2126
  if (this.seen.has(object)) return tagged("cycle");
2114
2127
  const opaque = encodeOpaqueObject(object);
2115
2128
  if (opaque !== void 0) return opaque;
2116
- if (depth >= this.options.maxDepth) return describeTooDeep(object);
2129
+ if (depth >= this.options.maxDepth || this.exhausted) return describeTooDeep(object);
2117
2130
  this.seen.add(object);
2118
2131
  try {
2119
2132
  if (Array.isArray(object)) return this.array(object, depth, path);
@@ -2127,14 +2140,16 @@ var Encoder = class {
2127
2140
  array(array, depth, path) {
2128
2141
  const items = [];
2129
2142
  const count = Math.min(array.length, this.options.maxItems);
2130
- for (let i = 0; i < count; i++) items.push(this.value(array[i], depth + 1, `${path}[${i}]`));
2131
- if (array.length > count) items.push(tagged("more", { n: array.length - count }));
2143
+ for (let i = 0; i < count && !this.exhausted; i++) {
2144
+ items.push(this.value(array[i], depth + 1, `${path}[${i}]`));
2145
+ }
2146
+ if (array.length > items.length) items.push(tagged("more", { n: array.length - items.length }));
2132
2147
  return items;
2133
2148
  }
2134
2149
  map(map, depth, path) {
2135
2150
  const entries = [];
2136
2151
  for (const [key, entryValue] of map) {
2137
- if (entries.length >= this.options.maxItems) break;
2152
+ if (entries.length >= this.options.maxItems || this.exhausted) break;
2138
2153
  entries.push([
2139
2154
  this.value(key, depth + 1, `${path}.<key>`),
2140
2155
  this.value(entryValue, depth + 1, `${path}.${String(key)}`)
@@ -2145,7 +2160,7 @@ var Encoder = class {
2145
2160
  set(set, depth, path) {
2146
2161
  const values = [];
2147
2162
  for (const item of set) {
2148
- if (values.length >= this.options.maxItems) break;
2163
+ if (values.length >= this.options.maxItems || this.exhausted) break;
2149
2164
  values.push(this.value(item, depth + 1, `${path}[]`));
2150
2165
  }
2151
2166
  return tagged("set", { values, size: set.size });
@@ -2154,11 +2169,14 @@ var Encoder = class {
2154
2169
  const fields = {};
2155
2170
  const keys = Object.keys(object);
2156
2171
  const count = Math.min(keys.length, this.options.maxKeys);
2157
- for (let i = 0; i < count; i++) {
2158
- const key = keys[i];
2172
+ let encodedCount = 0;
2173
+ for (; encodedCount < count && !this.exhausted; encodedCount++) {
2174
+ const key = keys[encodedCount];
2159
2175
  fields[key] = this.field(object, key, depth, path ? `${path}.${key}` : key);
2160
2176
  }
2161
- if (keys.length > count) fields["\u2026"] = tagged("more", { n: keys.length - count });
2177
+ if (keys.length > encodedCount) {
2178
+ fields["\u2026"] = tagged("more", { n: keys.length - encodedCount });
2179
+ }
2162
2180
  const className = describeClassName(object);
2163
2181
  if (className) return tagged("inst", { cls: className, v: fields });
2164
2182
  if ("$t" in fields) return tagged("obj", { v: fields });
@@ -2566,7 +2584,13 @@ var TraceUnits = class {
2566
2584
  };
2567
2585
 
2568
2586
  // src/trace/traceValues.ts
2569
- var TRACE_ENCODING = { maxDepth: 8, maxKeys: 200, maxItems: 500, maxString: 2e4 };
2587
+ var TRACE_ENCODING = {
2588
+ maxDepth: 8,
2589
+ maxKeys: 200,
2590
+ maxItems: 500,
2591
+ maxString: 2e4,
2592
+ maxNodes: 2e5
2593
+ };
2570
2594
  function isSensitiveName(name) {
2571
2595
  return maskByKey(name.replace(/^\$/, ""));
2572
2596
  }
@@ -3222,8 +3246,8 @@ var DevtoolsEdits = class {
3222
3246
  };
3223
3247
 
3224
3248
  // src/agent/encodeTransaction.ts
3225
- var STEP_ENCODING = { maxDepth: 3, maxKeys: 12, maxItems: 12, maxString: 160 };
3226
- var FULL_ENCODING = { maxDepth: 6, maxKeys: 80, maxItems: 80, maxString: 2e3 };
3249
+ var STEP_ENCODING = { maxDepth: 3, maxKeys: 12, maxItems: 12, maxString: 160, maxNodes: 60 };
3250
+ var FULL_ENCODING = { maxDepth: 6, maxKeys: 80, maxItems: 80, maxString: 2e3, maxNodes: 1500 };
3227
3251
  function noteText(note, texts2) {
3228
3252
  switch (note) {
3229
3253
  case "same":
@@ -3466,7 +3490,7 @@ var HealthMonitor = class {
3466
3490
  };
3467
3491
 
3468
3492
  // src/agent/options.ts
3469
- var AGENT_VERSION = "0.1.0";
3493
+ var AGENT_VERSION = "0.1.3";
3470
3494
  var DEFAULT_MAX_TRANSACTIONS = 1e3;
3471
3495
  var DEFAULT_GRACE_MS = 1e4;
3472
3496
 
@@ -3606,6 +3630,7 @@ function describeModel(instance, name, isKnownUnit) {
3606
3630
  // src/agent/panelConnection.ts
3607
3631
  var UNITS_PER_MESSAGE = 2e3;
3608
3632
  var TRANSACTIONS_PER_MESSAGE = 200;
3633
+ var TRANSACTION_CHARS_PER_MESSAGE = 4 * 1024 * 1024;
3609
3634
  var MAX_UNSENT_TRANSACTIONS = 2e3;
3610
3635
  var FLUSH_DELAY_MS = 40;
3611
3636
  function requestIdOf(command) {
@@ -3618,6 +3643,23 @@ function chunks(items, size) {
3618
3643
  }
3619
3644
  return result;
3620
3645
  }
3646
+ function transactionBatches(txs, maxCount = TRANSACTIONS_PER_MESSAGE, maxChars = TRANSACTION_CHARS_PER_MESSAGE) {
3647
+ const batches = [];
3648
+ let batch = [];
3649
+ let batchChars = 0;
3650
+ for (const tx of txs) {
3651
+ const chars = JSON.stringify(tx).length;
3652
+ if (batch.length && (batch.length >= maxCount || batchChars + chars > maxChars)) {
3653
+ batches.push(batch);
3654
+ batch = [];
3655
+ batchChars = 0;
3656
+ }
3657
+ batch.push(tx);
3658
+ batchChars += chars;
3659
+ }
3660
+ if (batch.length) batches.push(batch);
3661
+ return batches;
3662
+ }
3621
3663
  var PanelConnection = class {
3622
3664
  constructor(parts) {
3623
3665
  __publicField(this, "parts", parts);
@@ -3642,9 +3684,9 @@ var PanelConnection = class {
3642
3684
  this.sendTimeline();
3643
3685
  }
3644
3686
  if (this.unsentTransactions.length) {
3645
- const txs = this.unsentTransactions.map((transaction) => this.parts.encode(transaction));
3687
+ const transactions = this.unsentTransactions;
3646
3688
  this.unsentTransactions = [];
3647
- this.send({ type: "txs", txs });
3689
+ this.sendTransactions(transactions);
3648
3690
  }
3649
3691
  if (diagnostics.hasUnsentChanges) {
3650
3692
  diagnostics.hasUnsentChanges = false;
@@ -3700,6 +3742,10 @@ var PanelConnection = class {
3700
3742
  if (!this.isListening || this.flushTimer) return;
3701
3743
  this.flushTimer = setTimeout(this.flush, FLUSH_DELAY_MS);
3702
3744
  }
3745
+ sendTransactions(transactions) {
3746
+ const txs = transactions.map((transaction) => this.parts.encode(transaction));
3747
+ for (const batch of transactionBatches(txs)) this.send({ type: "txs", txs: batch });
3748
+ }
3703
3749
  sendUnits(units) {
3704
3750
  for (const chunk of chunks(units, UNITS_PER_MESSAGE))
3705
3751
  this.send({ type: "units", units: chunk });
@@ -3715,9 +3761,7 @@ var PanelConnection = class {
3715
3761
  this.sendUnits(units);
3716
3762
  scopes.hasUnsentChanges = false;
3717
3763
  this.send({ type: "scopes", scopes: scopes.list() });
3718
- const txs = history.transactions.map((transaction) => this.parts.encode(transaction));
3719
- for (const chunk of chunks(txs, TRANSACTIONS_PER_MESSAGE))
3720
- this.send({ type: "txs", txs: chunk });
3764
+ this.sendTransactions(history.transactions);
3721
3765
  this.parts.refreshStaticDiagnostics();
3722
3766
  diagnostics.hasUnsentChanges = false;
3723
3767
  this.send({ type: "diagnostics", items: diagnostics.list() });
@@ -0,0 +1,23 @@
1
+ import {
2
+ registerScope
3
+ } from "./chunk-GAQI7G4H.js";
4
+
5
+ // src/adapters/reactProvider.ts
6
+ import { createElement, useEffect, useState } from "react";
7
+ function createDeffectorProvider(Provider) {
8
+ function DeffectorProvider({ value, name = void 0, children = null }) {
9
+ const [scope, setScope] = useState(value);
10
+ const [appScope, setAppScope] = useState(value);
11
+ if (appScope !== value) {
12
+ setAppScope(value);
13
+ setScope(value);
14
+ }
15
+ useEffect(() => registerScope(value, { name, switcher: setScope }), [value, name]);
16
+ return createElement(Provider, { value: scope }, children);
17
+ }
18
+ return DeffectorProvider;
19
+ }
20
+
21
+ export {
22
+ createDeffectorProvider
23
+ };
@@ -0,0 +1,12 @@
1
+ // src/adapters/reactNoopProvider.ts
2
+ import { createElement } from "react";
3
+ function createNoopProvider(Provider) {
4
+ function DeffectorProvider({ value, children = null }) {
5
+ return createElement(Provider, { value }, children);
6
+ }
7
+ return DeffectorProvider;
8
+ }
9
+
10
+ export {
11
+ createNoopProvider
12
+ };
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  getAgent,
6
6
  registerModel,
7
7
  registerScope
8
- } from "./chunk-RRJRDKTW.js";
8
+ } from "./chunk-GAQI7G4H.js";
9
9
  import "./chunk-PKBMQBKP.js";
10
10
  export {
11
11
  Agent,
package/dist/node.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  getAgent,
5
5
  registerModel,
6
6
  registerScope
7
- } from "./chunk-RRJRDKTW.js";
7
+ } from "./chunk-GAQI7G4H.js";
8
8
  import "./chunk-PKBMQBKP.js";
9
9
 
10
10
  // src/node.ts
@@ -1,14 +1,11 @@
1
1
  import * as react from 'react';
2
- import { ReactNode } from 'react';
3
- import { Scope } from 'effector';
2
+ import * as effector from 'effector';
3
+ import { D as DeffectorProviderProps } from './reactTypes-DKgFbXjf.js';
4
4
 
5
- interface DeffectorProviderProps {
6
- value: Scope;
7
- /** accepted for the same API as the development build */
8
- name?: string;
9
- children?: ReactNode;
10
- }
11
5
  /** Production build: plain effector-react Provider. */
12
- declare function DeffectorProvider({ value, children }: DeffectorProviderProps): react.JSX.Element;
6
+ declare const DeffectorProvider: ({ value, children }: DeffectorProviderProps) => react.ReactElement<{
7
+ value: effector.Scope;
8
+ children?: react.ReactNode;
9
+ }, string | react.JSXElementConstructor<any>>;
13
10
 
14
- export { DeffectorProvider, DeffectorProvider as default };
11
+ export { DeffectorProvider, DeffectorProviderProps, DeffectorProvider as default };
@@ -1,11 +1,11 @@
1
+ import {
2
+ createNoopProvider
3
+ } from "./chunk-QTLW5ZH3.js";
1
4
  import "./chunk-PKBMQBKP.js";
2
5
 
3
6
  // src/adapters/react-noop.tsx
4
7
  import { Provider } from "effector-react";
5
- import { jsx } from "react/jsx-runtime";
6
- function DeffectorProvider({ value, children = null }) {
7
- return /* @__PURE__ */ jsx(Provider, { value, children });
8
- }
8
+ var DeffectorProvider = createNoopProvider(Provider);
9
9
  var react_noop_default = DeffectorProvider;
10
10
  export {
11
11
  DeffectorProvider,
@@ -0,0 +1,11 @@
1
+ import * as react from 'react';
2
+ import * as effector from 'effector';
3
+ import { D as DeffectorProviderProps } from './reactTypes-DKgFbXjf.js';
4
+
5
+ /** Production build: plain `effector-react/scope` Provider. */
6
+ declare const DeffectorProvider: ({ value, children }: DeffectorProviderProps) => react.ReactElement<{
7
+ value: effector.Scope;
8
+ children?: react.ReactNode;
9
+ }, string | react.JSXElementConstructor<any>>;
10
+
11
+ export { DeffectorProvider, DeffectorProviderProps, DeffectorProvider as default };
@@ -0,0 +1,13 @@
1
+ import {
2
+ createNoopProvider
3
+ } from "./chunk-QTLW5ZH3.js";
4
+ import "./chunk-PKBMQBKP.js";
5
+
6
+ // src/adapters/react-scope-noop.ts
7
+ import { Provider } from "effector-react/scope";
8
+ var DeffectorProvider = createNoopProvider(Provider);
9
+ var react_scope_noop_default = DeffectorProvider;
10
+ export {
11
+ DeffectorProvider,
12
+ react_scope_noop_default as default
13
+ };
@@ -0,0 +1,10 @@
1
+ import * as react from 'react';
2
+ import * as effector from 'effector';
3
+ import { D as DeffectorProviderProps } from './reactTypes-DKgFbXjf.js';
4
+
5
+ declare const DeffectorProvider: ({ value, name, children }: DeffectorProviderProps) => react.ReactElement<{
6
+ value: effector.Scope;
7
+ children?: react.ReactNode;
8
+ }, string | react.JSXElementConstructor<any>>;
9
+
10
+ export { DeffectorProvider, DeffectorProviderProps, DeffectorProvider as default };
@@ -0,0 +1,14 @@
1
+ import {
2
+ createDeffectorProvider
3
+ } from "./chunk-N4XUKHSM.js";
4
+ import "./chunk-GAQI7G4H.js";
5
+ import "./chunk-PKBMQBKP.js";
6
+
7
+ // src/adapters/react-scope.ts
8
+ import { Provider } from "effector-react/scope";
9
+ var DeffectorProvider = createDeffectorProvider(Provider);
10
+ var react_scope_default = DeffectorProvider;
11
+ export {
12
+ DeffectorProvider,
13
+ react_scope_default as default
14
+ };
package/dist/react.d.ts CHANGED
@@ -1,17 +1,11 @@
1
1
  import * as react from 'react';
2
- import { ReactNode } from 'react';
3
- import { Scope } from 'effector';
2
+ import * as effector from 'effector';
3
+ import { D as DeffectorProviderProps } from './reactTypes-DKgFbXjf.js';
4
4
 
5
- interface DeffectorProviderProps {
6
- value: Scope;
7
- name?: string;
8
- children?: ReactNode;
9
- }
10
- /**
11
- * Drop-in replacement for effector-react's Provider.
12
- * deffector can render the app with another scope through it: a forked copy
13
- * of the past for time travel, or a fresh scope for replaying a trace.
14
- */
15
- declare function DeffectorProvider({ value, name, children, }: DeffectorProviderProps): react.JSX.Element;
5
+ /** Drop-in replacement for effector-react's Provider (see reactProvider.ts). */
6
+ declare const DeffectorProvider: ({ value, name, children }: DeffectorProviderProps) => react.ReactElement<{
7
+ value: effector.Scope;
8
+ children?: react.ReactNode;
9
+ }, string | react.JSXElementConstructor<any>>;
16
10
 
17
- export { DeffectorProvider, DeffectorProvider as default };
11
+ export { DeffectorProvider, DeffectorProviderProps, DeffectorProvider as default };
package/dist/react.js CHANGED
@@ -1,26 +1,12 @@
1
1
  import {
2
- registerScope
3
- } from "./chunk-RRJRDKTW.js";
2
+ createDeffectorProvider
3
+ } from "./chunk-N4XUKHSM.js";
4
+ import "./chunk-GAQI7G4H.js";
4
5
  import "./chunk-PKBMQBKP.js";
5
6
 
6
7
  // src/adapters/react.tsx
7
- import { useEffect, useState } from "react";
8
8
  import { Provider } from "effector-react";
9
- import { jsx } from "react/jsx-runtime";
10
- function DeffectorProvider({
11
- value,
12
- name = void 0,
13
- children = null
14
- }) {
15
- const [scope, setScope] = useState(value);
16
- const [appScope, setAppScope] = useState(value);
17
- if (appScope !== value) {
18
- setAppScope(value);
19
- setScope(value);
20
- }
21
- useEffect(() => registerScope(value, { name, switcher: setScope }), [value, name]);
22
- return /* @__PURE__ */ jsx(Provider, { value: scope, children });
23
- }
9
+ var DeffectorProvider = createDeffectorProvider(Provider);
24
10
  var react_default = DeffectorProvider;
25
11
  export {
26
12
  DeffectorProvider,
@@ -0,0 +1,11 @@
1
+ import { ReactNode } from 'react';
2
+ import { Scope } from 'effector';
3
+
4
+ interface DeffectorProviderProps {
5
+ value: Scope;
6
+ /** shown in the panel's scope selector */
7
+ name?: string;
8
+ children?: ReactNode;
9
+ }
10
+
11
+ export type { DeffectorProviderProps as D };
package/dist/vue.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  registerScope
3
- } from "./chunk-RRJRDKTW.js";
3
+ } from "./chunk-GAQI7G4H.js";
4
4
  import "./chunk-PKBMQBKP.js";
5
5
 
6
6
  // src/adapters/vue.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deffector/agent",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "DevTools for effector: every user action as one transaction, time travel, state injection, effect overrides and replayable traces. The agent that runs in your app.",
5
5
  "keywords": [
6
6
  "effector",
@@ -42,6 +42,12 @@
42
42
  "@deffector/source": "./src/adapters/react.tsx",
43
43
  "default": "./dist/react.js"
44
44
  },
45
+ "./react-scope": {
46
+ "types": "./dist/react-scope.d.ts",
47
+ "production": "./dist/react-scope-noop.js",
48
+ "@deffector/source": "./src/adapters/react-scope.ts",
49
+ "default": "./dist/react-scope.js"
50
+ },
45
51
  "./vue": {
46
52
  "types": "./dist/vue.d.ts",
47
53
  "production": "./dist/vue-noop.js",
@@ -74,10 +80,10 @@
74
80
  },
75
81
  "peerDependencies": {
76
82
  "effector": ">=22.6.0",
77
- "effector-react": ">=23",
83
+ "effector-react": ">=22",
78
84
  "effector-solid": ">=0.23",
79
85
  "effector-vue": ">=23",
80
- "react": ">=18",
86
+ "react": ">=16.8",
81
87
  "solid-js": ">=1.8",
82
88
  "vue": ">=3.3"
83
89
  },