@shaper.org/core 1.1.1 → 1.2.0-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.
@@ -1,58 +1,159 @@
1
1
  import { ErrorPayload, FullReloadPayload, PrunePayload, UpdatePayload } from "vite/types/hmrPayload.js";
2
2
  import { ViteHotContext } from "vite/types/hot.js";
3
3
 
4
- //#region src/client/iframe-post-message.d.ts
5
- type PostMessageType = "server-ready" | "all-routes" | "route-change" | "route-refresh" | "error" | "inspector-tree-update" | "inspector-selected-node" | "inspector-reset-inline-style";
6
- interface PostMessage {
4
+ //#region src/client/vite-hook.d.ts
5
+ type ViteUpdateCallback = ((data: UpdatePayload, timestamp: number) => void) | null;
6
+ type VitePruneCallback = ((data: PrunePayload, timestamp: number) => void) | null;
7
+ type VitFullReloadCallback = ((data: FullReloadPayload, timestamp: number) => void) | null;
8
+ type ViteErrorCallback = ((data: ErrorPayload, timestamp: number) => void) | null;
9
+ type ViteInvalidateCallback = ((data: any, timestamp: number) => void) | null;
10
+ type ViteWebSocketConnectCallback = ((data: any, timestamp: number) => void) | null;
11
+ type ViteWebSocketDisconnectCallback = ((data: any, timestamp: number) => void) | null;
12
+ declare class ViteHook {
13
+ private hmr;
14
+ constructor();
15
+ hotExists(hmr: ViteHotContext | undefined): hmr is ViteHotContext;
16
+ onBeforeUpdate(fn?: ViteUpdateCallback): void;
17
+ onAfterUpdate(fn?: ViteUpdateCallback): void;
18
+ onBeforePrune(fn?: VitePruneCallback): void;
19
+ onBeforeFullReload(fn?: VitFullReloadCallback): void;
20
+ onError(fn?: ViteErrorCallback): void;
21
+ onInvalidate(fn?: ViteInvalidateCallback): void;
22
+ onWsConnect(fn?: ViteWebSocketConnectCallback): void;
23
+ onWsDisconnect(fn?: ViteWebSocketDisconnectCallback): void;
24
+ }
25
+ //#endregion
26
+ //#region src/client/types/base.d.ts
27
+ interface BasePostMessage {
7
28
  message: any;
8
- type: PostMessageType;
29
+ type: string;
9
30
  object: "shaper-post-message";
10
31
  }
11
- interface RouteInfo {
12
- name: string;
13
- path: string;
14
- file: string;
15
- }
16
- interface ServerReadyMessage extends PostMessage {
32
+ interface ServerReadyMessage extends BasePostMessage {
17
33
  type: "server-ready";
18
34
  message: {
19
35
  satus: "ok";
20
36
  };
21
37
  }
22
- interface AllRoutesMessage extends PostMessage {
38
+ //#endregion
39
+ //#region src/client/types/inspector.d.ts
40
+ interface SelectedNode {
41
+ file: string;
42
+ line: number;
43
+ column: number;
44
+ attributes: Record<string, string>;
45
+ }
46
+ interface InspectorSelectedNodeMessage extends BasePostMessage {
47
+ type: "inspector-selected-node";
48
+ message: SelectedNode;
49
+ }
50
+ interface InspectorNode {
51
+ id: string;
52
+ tag: string;
53
+ children: InspectorNode[];
54
+ }
55
+ interface InspectorTreeUpdateMessage extends BasePostMessage {
56
+ type: "inspector-tree-update";
57
+ message: {
58
+ tree: InspectorNode;
59
+ };
60
+ }
61
+ //#endregion
62
+ //#region src/client/types/network.d.ts
63
+ interface NetworkRequest {
64
+ url: string;
65
+ method: string;
66
+ status: number;
67
+ statusText: string;
68
+ requestBody: string | undefined;
69
+ timestamp: string;
70
+ origin: string;
71
+ headers: Record<string, string>;
72
+ responseBody: string | undefined;
73
+ duration: number;
74
+ }
75
+ interface NetworkRequestMessage extends BasePostMessage {
76
+ message: NetworkRequest;
77
+ type: "network-request";
78
+ }
79
+ interface NetworkRequestChunkMessage extends BasePostMessage {
80
+ message: NetworkRequest;
81
+ type: "network-request-chunk";
82
+ }
83
+ //#endregion
84
+ //#region src/client/types/route-event.d.ts
85
+ interface RouteInfo {
86
+ name: string;
87
+ path: string;
88
+ file: string;
89
+ }
90
+ interface AllRoutesMessage extends BasePostMessage {
23
91
  type: "all-routes";
24
92
  message: {
25
93
  routes: RouteInfo[];
26
94
  };
27
95
  }
28
- interface RouteChangeMessage extends PostMessage {
96
+ interface RouteChangeMessage extends BasePostMessage {
29
97
  type: "route-change";
30
98
  message: RouteInfo;
31
99
  }
32
- interface RouteRefreshMessage extends PostMessage {
100
+ interface RouteRefreshMessage extends BasePostMessage {
33
101
  type: "route-refresh";
34
102
  message: RouteInfo;
35
103
  }
36
- interface ErrorInfo {
37
- errorType: string;
104
+ //#endregion
105
+ //#region src/client/types/runtime.d.ts
106
+ interface ConsoleLog {
107
+ level: "log" | "warn" | "error";
108
+ message: string;
109
+ fingerprint: string;
38
110
  timestamp: number;
39
- lineno?: number;
40
- colno?: number;
111
+ type: "CONSOLE";
112
+ }
113
+ interface ConsoleMessage extends BasePostMessage {
114
+ message: ConsoleLog;
115
+ type: "console";
116
+ }
117
+ interface UnhandledRejection {
118
+ message: string;
41
119
  stack: string;
42
- has_blank_screen: boolean;
43
- filename?: string;
120
+ readyState: string;
121
+ userAgent: string;
122
+ rootPresent: boolean;
123
+ hasBlankScreen: boolean;
124
+ scripts: string[];
125
+ fingerprint: string;
126
+ timestamp: number;
127
+ type: "UNHANDLED_REJECTION";
44
128
  }
45
- interface SelectedNode {
46
- file: string;
47
- line: number;
48
- column: number;
49
- attributes: Record<string, string>;
129
+ interface UnhandledRejectionMessage extends BasePostMessage {
130
+ message: UnhandledRejection;
131
+ type: "unhandled-rejection";
50
132
  }
51
- interface InspectorNode {
52
- id: string;
53
- tag: string;
54
- children: InspectorNode[];
133
+ interface RuntimeError {
134
+ message: string;
135
+ lineno: number;
136
+ colno: number;
137
+ filename: string | undefined;
138
+ stack: string;
139
+ readyState: DocumentReadyState;
140
+ userAgent: string;
141
+ rootPresent: boolean;
142
+ scripts: string[];
143
+ hasBlankScreen: boolean;
144
+ fingerprint: string;
145
+ timestamp: number;
146
+ type: "RUNTIME_ERROR";
55
147
  }
148
+ interface RuntimeErrorMessage extends BasePostMessage {
149
+ message: RuntimeError;
150
+ type: "runtime-error";
151
+ }
152
+ //#endregion
153
+ //#region src/client/types/message.d.ts
154
+ type PostMessage = ConsoleMessage | UnhandledRejectionMessage | RuntimeErrorMessage | AllRoutesMessage | RouteRefreshMessage | RouteChangeMessage | ServerReadyMessage | InspectorSelectedNodeMessage | InspectorTreeUpdateMessage | NetworkRequestMessage | NetworkRequestChunkMessage;
155
+ //#endregion
156
+ //#region src/client/iframe-post-message.d.ts
56
157
  declare class IframePostMessageClient {
57
158
  authHost: string;
58
159
  private receiveCallbacks;
@@ -68,8 +169,10 @@ declare class IframePostMessageClient {
68
169
  sendAllRoutes(routes: RouteInfo[]): void;
69
170
  sendRouteChange(route: RouteInfo): void;
70
171
  sendRouteRefresh(route: RouteInfo): void;
71
- sendError(errorInfo: ErrorInfo): void;
72
- sendConsole(): void;
172
+ sendRuntimeError(runtimeError: RuntimeError): void;
173
+ sendUnhandledRejection(error: UnhandledRejection): void;
174
+ sendConsoleMessage(consoleMessage: ConsoleLog): void;
175
+ sendNetworkRequest(request: NetworkRequest): void;
73
176
  sendSelectedNode(node: SelectedNode): void;
74
177
  sendTree(tree: InspectorNode): void;
75
178
  }
@@ -86,59 +189,114 @@ interface WorkspaceModeMessage extends ReceiveBaseMessage {
86
189
  };
87
190
  }
88
191
  declare const isWorkspaceModeEvent: (event: MessageEvent<any>) => event is MessageEvent<WorkspaceModeMessage>;
89
- interface NodeAttributesPatchMessage extends ReceiveBaseMessage {
90
- type: "NODE_ATTRIBUTES_PATCH";
192
+ interface SetSelectNodeMessage extends ReceiveBaseMessage {
193
+ type: "SET_SELECT_NODE";
194
+ data: {
195
+ id: string;
196
+ };
197
+ }
198
+ declare const isSetSelectNodeEvent: (event: MessageEvent<any>) => event is MessageEvent<SetSelectNodeMessage>;
199
+ interface SetHoverNodeMessage extends ReceiveBaseMessage {
200
+ type: "SET_HOVER_NODE";
91
201
  data: {
92
202
  id: string;
93
- property: string;
94
- value: string;
95
203
  };
96
204
  }
97
- declare const isNodeAttributesPatchEvent: (event: MessageEvent<any>) => event is MessageEvent<NodeAttributesPatchMessage>;
98
- interface NodeAttributesPatchBatchMessage extends ReceiveBaseMessage {
99
- type: "NODE_ATTRIBUTES_PATCH_BATCH";
205
+ declare const isSetHoverNodeEvent: (event: MessageEvent<any>) => event is MessageEvent<SetSelectNodeMessage>;
206
+ interface UpdatePropsEvent {
207
+ type: "update_props";
100
208
  data: {
101
- selector: string;
209
+ id: string;
102
210
  property: string;
103
211
  value: string;
104
- }[];
212
+ };
105
213
  }
106
- declare const isNodeAttributesPatchBatchEvent: (event: MessageEvent<any>) => event is MessageEvent<NodeAttributesPatchBatchMessage>;
107
- interface SetSelectNodeMessage extends ReceiveBaseMessage {
108
- type: "SET_SELECT_NODE";
214
+ interface AddFontEvent {
215
+ type: "add_font";
109
216
  data: {
110
217
  id: string;
218
+ url: string;
111
219
  };
112
220
  }
113
- declare const isSetSelectNodeEvent: (event: MessageEvent<any>) => event is MessageEvent<SetSelectNodeMessage>;
114
- interface SetHoverNodeMessage extends ReceiveBaseMessage {
115
- type: "SET_HOVER_NODE";
221
+ interface SetTextContentEvent {
222
+ type: "set_text_content";
116
223
  data: {
117
224
  id: string;
225
+ content: string;
118
226
  };
119
227
  }
120
- declare const isSetHoverNodeEvent: (event: MessageEvent<any>) => event is MessageEvent<SetSelectNodeMessage>;
121
- //#endregion
122
- //#region src/client/vite-hook.d.ts
123
- type ViteUpdateCallback = ((data: UpdatePayload, timestamp: number) => void) | null;
124
- type VitePruneCallback = ((data: PrunePayload, timestamp: number) => void) | null;
125
- type VitFullReloadCallback = ((data: FullReloadPayload, timestamp: number) => void) | null;
126
- type ViteErrorCallback = ((data: ErrorPayload, timestamp: number) => void) | null;
127
- type ViteInvalidateCallback = ((data: any, timestamp: number) => void) | null;
128
- type ViteWebSocketConnectCallback = ((data: any, timestamp: number) => void) | null;
129
- type ViteWebSocketDisconnectCallback = ((data: any, timestamp: number) => void) | null;
130
- declare class ViteHook {
131
- private hmr;
132
- constructor();
133
- hotExists(hmr: ViteHotContext | undefined): hmr is ViteHotContext;
134
- onBeforeUpdate(fn?: ViteUpdateCallback): void;
135
- onAfterUpdate(fn?: ViteUpdateCallback): void;
136
- onBeforePrune(fn?: VitePruneCallback): void;
137
- onBeforeFullReload(fn?: VitFullReloadCallback): void;
138
- onError(fn?: ViteErrorCallback): void;
139
- onInvalidate(fn?: ViteInvalidateCallback): void;
140
- onWsConnect(fn?: ViteWebSocketConnectCallback): void;
141
- onWsDisconnect(fn?: ViteWebSocketDisconnectCallback): void;
228
+ type PatchEvent = UpdatePropsEvent | AddFontEvent | SetTextContentEvent;
229
+ interface UpdateBatchMessage extends ReceiveBaseMessage {
230
+ type: "UPDATE_BATCH";
231
+ data: {
232
+ events: PatchEvent[];
233
+ };
142
234
  }
235
+ declare const isUpdateBatchEvent: (event: MessageEvent<any>) => event is MessageEvent<UpdateBatchMessage>;
236
+ //#endregion
237
+ //#region src/client/error-parser.d.ts
238
+ declare function serialize(value: unknown, depth?: number, maxDepth?: number, seen?: WeakSet<any>): any;
239
+ declare function hasMatchingStackFrame(error: unknown): boolean;
240
+ declare function formatReason(response: Response): string;
241
+ declare function extractResourceError(event: Event): {
242
+ message: string;
243
+ lineno: number;
244
+ colno: number;
245
+ filename: string;
246
+ stack: string;
247
+ readyState: DocumentReadyState;
248
+ userAgent: string;
249
+ rootPresent: boolean;
250
+ scripts: string[];
251
+ } | null;
252
+ declare function getRuntimeFingerprint(errorEvent: ErrorEvent): string;
253
+ declare function parseError({
254
+ message,
255
+ lineno,
256
+ colno,
257
+ filename,
258
+ error
259
+ }: {
260
+ message: string;
261
+ lineno: number;
262
+ colno: number;
263
+ filename?: string;
264
+ error?: any;
265
+ }): {
266
+ message: string;
267
+ lineno: number;
268
+ colno: number;
269
+ filename: string | undefined;
270
+ stack: any;
271
+ readyState: DocumentReadyState;
272
+ userAgent: string;
273
+ rootPresent: boolean;
274
+ scripts: string[];
275
+ };
276
+ declare function parseViteError({
277
+ message,
278
+ lineno,
279
+ colno,
280
+ filename,
281
+ stack,
282
+ frame
283
+ }: {
284
+ message: string;
285
+ lineno?: number;
286
+ colno?: number;
287
+ filename?: string;
288
+ stack: any;
289
+ frame?: string;
290
+ }): {
291
+ message: string;
292
+ lineno: number;
293
+ colno: number;
294
+ filename: string | undefined;
295
+ stack: any;
296
+ readyState: DocumentReadyState;
297
+ userAgent: string;
298
+ rootPresent: boolean;
299
+ scripts: string[];
300
+ };
143
301
  //#endregion
144
- export { AllRoutesMessage, IframePostMessageClient, InspectorNode, NodeAttributesPatchBatchMessage, NodeAttributesPatchMessage, PostMessage, PostMessageType, RouteChangeMessage, RouteInfo, RouteRefreshMessage, ServerReadyMessage, SetHoverNodeMessage, SetSelectNodeMessage, ViteHook, isNodeAttributesPatchBatchEvent, isNodeAttributesPatchEvent, isSetHoverNodeEvent, isSetSelectNodeEvent, isWorkspaceModeEvent };
302
+ export { AddFontEvent, AllRoutesMessage, BasePostMessage, ConsoleLog, ConsoleMessage, IframePostMessageClient, InspectorNode, InspectorSelectedNodeMessage, InspectorTreeUpdateMessage, NetworkRequest, NetworkRequestChunkMessage, NetworkRequestMessage, PatchEvent, PostMessage, ReceiveBaseMessage, RouteChangeMessage, RouteInfo, RouteRefreshMessage, RuntimeError, RuntimeErrorMessage, SelectedNode, ServerReadyMessage, SetHoverNodeMessage, SetSelectNodeMessage, SetTextContentEvent, UnhandledRejection, UnhandledRejectionMessage, UpdateBatchMessage, UpdatePropsEvent, ViteHook, WorkspaceModeMessage, extractResourceError, formatReason, getRuntimeFingerprint, hasMatchingStackFrame, isSetHoverNodeEvent, isSetSelectNodeEvent, isUpdateBatchEvent, isWorkspaceModeEvent, parseError, parseViteError, serialize };
@@ -1,3 +1,81 @@
1
+ //#region src/client/vite-hook.ts
2
+ var ViteHook = class {
3
+ hmr;
4
+ constructor() {
5
+ this.hmr = import.meta.hot;
6
+ }
7
+ hotExists(hmr) {
8
+ if (hmr) return true;
9
+ console.error("HotContext does not exists");
10
+ return false;
11
+ }
12
+ onBeforeUpdate(fn = null) {
13
+ if (!this.hotExists(this.hmr)) return;
14
+ this.hmr.on("vite:beforeUpdate", (data) => {
15
+ const timestamp = Date.now();
16
+ console.debug("beforeUpdate");
17
+ fn && fn(data, timestamp);
18
+ });
19
+ }
20
+ onAfterUpdate(fn = null) {
21
+ if (!this.hotExists(this.hmr)) return;
22
+ this.hmr.on("vite:afterUpdate", (data) => {
23
+ const timestamp = Date.now();
24
+ console.debug("afterUpdate");
25
+ fn && fn(data, timestamp);
26
+ });
27
+ }
28
+ onBeforePrune(fn = null) {
29
+ if (!this.hotExists(this.hmr)) return;
30
+ this.hmr.on("vite:beforePrune", (data) => {
31
+ const timestamp = Date.now();
32
+ console.debug("beforePrune");
33
+ fn && fn(data, timestamp);
34
+ });
35
+ }
36
+ onBeforeFullReload(fn = null) {
37
+ if (!this.hotExists(this.hmr)) return;
38
+ this.hmr.on("vite:beforeFullReload", (data) => {
39
+ const timestamp = Date.now();
40
+ console.debug("beforeFullReload");
41
+ fn && fn(data, timestamp);
42
+ });
43
+ }
44
+ onError(fn = null) {
45
+ if (!this.hotExists(this.hmr)) return;
46
+ this.hmr.on("vite:error", (data) => {
47
+ const timestamp = Date.now();
48
+ console.debug("error");
49
+ fn && fn(data, timestamp);
50
+ });
51
+ }
52
+ onInvalidate(fn = null) {
53
+ if (!this.hotExists(this.hmr)) return;
54
+ this.hmr.on("vite:invalidate", (data) => {
55
+ const timestamp = Date.now();
56
+ console.debug("invalidate");
57
+ fn && fn(data, timestamp);
58
+ });
59
+ }
60
+ onWsConnect(fn = null) {
61
+ if (!this.hotExists(this.hmr)) return;
62
+ this.hmr.on("vite:ws:connect", (data) => {
63
+ const timestamp = Date.now();
64
+ console.debug("ws:connect");
65
+ fn && fn(data, timestamp);
66
+ });
67
+ }
68
+ onWsDisconnect(fn = null) {
69
+ if (!this.hotExists(this.hmr)) return;
70
+ this.hmr.on("vite:ws:disconnect", (data) => {
71
+ const timestamp = Date.now();
72
+ console.debug(":ws:disconnect");
73
+ fn && fn(data, timestamp);
74
+ });
75
+ }
76
+ };
77
+
78
+ //#endregion
1
79
  //#region src/client/iframe-post-message.ts
2
80
  var IframePostMessageClient = class {
3
81
  authHost;
@@ -14,7 +92,20 @@ var IframePostMessageClient = class {
14
92
  if (!route.file.startsWith("src")) throw Error(`Invalid route file, route file should be relative to the root got: ${route.file}`);
15
93
  }
16
94
  _send(message) {
17
- window.parent.postMessage(message, this.authHost);
95
+ if (!window.parent || !message || typeof message !== "object" || !message.object) return;
96
+ if ((() => {
97
+ try {
98
+ const fromAncestor = window.location?.ancestorOrigins?.item?.(0);
99
+ if (fromAncestor) return new URL(fromAncestor).origin;
100
+ } catch {}
101
+ try {
102
+ if (document.referrer) return new URL(document.referrer).origin;
103
+ } catch {}
104
+ return null;
105
+ })() !== this.authHost) return;
106
+ try {
107
+ window.parent.postMessage(message, this.authHost);
108
+ } catch {}
18
109
  }
19
110
  /**
20
111
  * Allow consumers to register callbacks for incoming messages.
@@ -47,12 +138,14 @@ var IframePostMessageClient = class {
47
138
  this._send(message);
48
139
  }
49
140
  sendRouteChange(route) {
141
+ console.log("routechange client", route);
50
142
  this._ensure_route_file(route);
51
143
  const message = {
52
144
  type: "route-change",
53
145
  message: { ...route },
54
146
  object: "shaper-post-message"
55
147
  };
148
+ console.log("routechange client message", message);
56
149
  this._send(message);
57
150
  }
58
151
  sendRouteRefresh(route) {
@@ -64,15 +157,38 @@ var IframePostMessageClient = class {
64
157
  };
65
158
  this._send(message);
66
159
  }
67
- sendError(errorInfo) {
160
+ sendRuntimeError(runtimeError) {
161
+ const message = {
162
+ type: "runtime-error",
163
+ message: runtimeError,
164
+ object: "shaper-post-message"
165
+ };
166
+ this._send(message);
167
+ }
168
+ sendUnhandledRejection(error) {
169
+ const message = {
170
+ type: "unhandled-rejection",
171
+ message: error,
172
+ object: "shaper-post-message"
173
+ };
174
+ this._send(message);
175
+ }
176
+ sendConsoleMessage(consoleMessage) {
68
177
  const message = {
69
- type: "error",
70
- message: errorInfo,
178
+ type: "console",
179
+ message: consoleMessage,
180
+ object: "shaper-post-message"
181
+ };
182
+ this._send(message);
183
+ }
184
+ sendNetworkRequest(request) {
185
+ const message = {
186
+ type: "network-request",
187
+ message: request,
71
188
  object: "shaper-post-message"
72
189
  };
73
190
  this._send(message);
74
191
  }
75
- sendConsole() {}
76
192
  sendSelectedNode(node) {
77
193
  const message = {
78
194
  type: "inspector-selected-node",
@@ -93,96 +209,156 @@ var IframePostMessageClient = class {
93
209
  const isWorkspaceModeEvent = (event) => {
94
210
  return event.data.type === "WORKSPACE_MODE";
95
211
  };
96
- const isNodeAttributesPatchEvent = (event) => {
97
- return event.data.type === "NODE_ATTRIBUTES_PATCH";
98
- };
99
- const isNodeAttributesPatchBatchEvent = (event) => {
100
- return event.data.type === "NODE_ATTRIBUTES_PATCH_BATCH";
101
- };
102
212
  const isSetSelectNodeEvent = (event) => {
103
213
  return event.data.type === "SET_SELECT_NODE";
104
214
  };
105
215
  const isSetHoverNodeEvent = (event) => {
106
216
  return event.data.type === "SET_HOVER_NODE";
107
217
  };
218
+ const isUpdateBatchEvent = (event) => {
219
+ return event.data.type === "UPDATE_BATCH";
220
+ };
108
221
 
109
222
  //#endregion
110
- //#region src/client/vite-hook.ts
111
- var ViteHook = class {
112
- hmr;
113
- constructor() {
114
- this.hmr = import.meta.hot;
115
- }
116
- hotExists(hmr) {
117
- if (hmr) return true;
118
- console.error("HotContext does not exists");
119
- return false;
120
- }
121
- onBeforeUpdate(fn = null) {
122
- if (!this.hotExists(this.hmr)) return;
123
- this.hmr.on("vite:beforeUpdate", (data) => {
124
- const timestamp = Date.now();
125
- console.debug("beforeUpdate");
126
- fn && fn(data, timestamp);
127
- });
128
- }
129
- onAfterUpdate(fn = null) {
130
- if (!this.hotExists(this.hmr)) return;
131
- this.hmr.on("vite:afterUpdate", (data) => {
132
- const timestamp = Date.now();
133
- console.debug("afterUpdate");
134
- fn && fn(data, timestamp);
135
- });
136
- }
137
- onBeforePrune(fn = null) {
138
- if (!this.hotExists(this.hmr)) return;
139
- this.hmr.on("vite:beforePrune", (data) => {
140
- const timestamp = Date.now();
141
- console.debug("beforePrune");
142
- fn && fn(data, timestamp);
143
- });
144
- }
145
- onBeforeFullReload(fn = null) {
146
- if (!this.hotExists(this.hmr)) return;
147
- this.hmr.on("vite:beforeFullReload", (data) => {
148
- const timestamp = Date.now();
149
- console.debug("beforeFullReload");
150
- fn && fn(data, timestamp);
151
- });
152
- }
153
- onError(fn = null) {
154
- if (!this.hotExists(this.hmr)) return;
155
- this.hmr.on("vite:error", (data) => {
156
- const timestamp = Date.now();
157
- console.debug("error");
158
- fn && fn(data, timestamp);
159
- });
160
- }
161
- onInvalidate(fn = null) {
162
- if (!this.hotExists(this.hmr)) return;
163
- this.hmr.on("vite:invalidate", (data) => {
164
- const timestamp = Date.now();
165
- console.debug("invalidate");
166
- fn && fn(data, timestamp);
167
- });
168
- }
169
- onWsConnect(fn = null) {
170
- if (!this.hotExists(this.hmr)) return;
171
- this.hmr.on("vite:ws:connect", (data) => {
172
- const timestamp = Date.now();
173
- console.debug("ws:connect");
174
- fn && fn(data, timestamp);
175
- });
176
- }
177
- onWsDisconnect(fn = null) {
178
- if (!this.hotExists(this.hmr)) return;
179
- this.hmr.on("vite:ws:disconnect", (data) => {
180
- const timestamp = Date.now();
181
- console.debug(":ws:disconnect");
182
- fn && fn(data, timestamp);
183
- });
223
+ //#region src/client/error-parser.ts
224
+ const VITE_DEP_PATH_REGEX = /\/(?:node_modules\/)?\.vite\/deps\//i;
225
+ const DYNAMIC_IMPORT_FAILURE_REGEX = /failed to fetch dynamically imported module|error loading dynamically imported module|failed to load module script|importing a module script failed|expected a javascript-or-wasm module script|failed to load resource|resource load failed|disallowed mime type|net::err_aborted/i;
226
+ const DIRECT_ERROR_FIELDS = [
227
+ "message",
228
+ "stack",
229
+ "filename",
230
+ "url",
231
+ "src",
232
+ "href"
233
+ ];
234
+ const NESTED_ERROR_FIELDS = [
235
+ "error",
236
+ "reason",
237
+ "cause"
238
+ ];
239
+ function serialize(value, depth = 0, maxDepth = 5, seen = /* @__PURE__ */ new WeakSet()) {
240
+ if (depth > maxDepth) return "[MaxDepthReached]";
241
+ if (value === null || value === void 0) return value;
242
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value;
243
+ if (typeof value === "bigint") return value.toString();
244
+ if (typeof value === "function") return `[Function ${value.name || "anonymous"}]`;
245
+ if (typeof value === "symbol") return value.toString();
246
+ if (typeof value === "object") {
247
+ if (seen.has(value)) return "[Circular]";
248
+ seen.add(value);
249
+ if (value instanceof Error) return {
250
+ name: value.name,
251
+ message: value.message,
252
+ stack: value.stack
253
+ };
254
+ if (Array.isArray(value)) return value.slice(0, 100).map((v) => serialize(v, depth + 1, maxDepth, seen));
255
+ const out = {};
256
+ for (const k of Object.keys(value).slice(0, 100)) out[k] = serialize(value[k], depth + 1, maxDepth, seen);
257
+ return out;
184
258
  }
185
- };
259
+ return String(value);
260
+ }
261
+ /**
262
+ * Converts arbitrary values into readable text.
263
+ */
264
+ function stringifyError(value) {
265
+ if (!value) return "";
266
+ if (typeof value === "string") return value;
267
+ if (Array.isArray(value)) return value.map(stringifyError).join("\n");
268
+ if (value instanceof Error) return [value.message, value.stack].filter(Boolean).join("\n");
269
+ if (typeof value === "object") return Object.values(value).map(stringifyError).join("\n");
270
+ return JSON.stringify(value);
271
+ }
272
+ /**
273
+ * Recursively extracts error-related strings
274
+ * from arbitrary nested structures.
275
+ */
276
+ function extractStackLines(value) {
277
+ if (!value) return [];
278
+ if (typeof value === "string") return [value];
279
+ if (value instanceof Error) return [stringifyError(value)];
280
+ if (Array.isArray(value)) return value.flatMap(extractStackLines);
281
+ if (typeof value !== "object") return [];
282
+ const object = value;
283
+ const directText = DIRECT_ERROR_FIELDS.map((field) => stringifyError(object[field])).filter(Boolean).join("\n");
284
+ const nestedText = NESTED_ERROR_FIELDS.flatMap((field) => extractStackLines(object[field]));
285
+ return directText ? [directText, ...nestedText] : nestedText;
286
+ }
287
+ function hasMatchingStackFrame(error) {
288
+ return extractStackLines(error).some((line) => {
289
+ const matchesViteChunkPath = VITE_DEP_PATH_REGEX.test(line);
290
+ const matchesDynamicImportFailure = DYNAMIC_IMPORT_FAILURE_REGEX.test(line);
291
+ return matchesViteChunkPath && matchesDynamicImportFailure;
292
+ });
293
+ }
294
+ function formatReason(response) {
295
+ const cleanedUrl = response.url ?? "";
296
+ const statusText = response.statusText && response.statusText.length > 0 ? response.statusText : "(no status text)";
297
+ let message = `Server responded with ${response.status} ${statusText}`;
298
+ if (cleanedUrl) message += ` at ${cleanedUrl}`;
299
+ return message;
300
+ }
301
+ function extractResourceError(event) {
302
+ const target = event.target;
303
+ if (!(target instanceof HTMLElement)) return null;
304
+ let url = "";
305
+ if ("src" in target && typeof target.src === "string") url = target.src;
306
+ else if ("href" in target && typeof target.href === "string") url = target.href;
307
+ if (!url) return null;
308
+ const payload = {
309
+ message: `Failed to load resource: ${url}`,
310
+ lineno: 0,
311
+ colno: 0,
312
+ filename: url,
313
+ stack: `Resource load failed at ${url}`,
314
+ readyState: document.readyState,
315
+ userAgent: navigator.userAgent,
316
+ rootPresent: !!document.getElementById("root"),
317
+ scripts: Array.from(document.scripts || []).slice(0, 25).map((script) => script.src ? script.src : script.textContent?.slice(0, 80) || "inline")
318
+ };
319
+ return hasMatchingStackFrame(payload) ? payload : null;
320
+ }
321
+ function getRuntimeFingerprint(errorEvent) {
322
+ const { lineno, colno, filename, message } = errorEvent;
323
+ return `${message}|${filename}|${lineno}|${colno}`;
324
+ }
325
+ function formatResponseError(response) {
326
+ const url = response.url ?? "";
327
+ const baseMessage = `Server responded with ${response.status} ${response.statusText || "(no status text)"}`;
328
+ return url ? `${baseMessage} at ${url}` : baseMessage;
329
+ }
330
+ function parseError({ message, lineno, colno, filename, error }) {
331
+ const scriptsSnapshot = Array.from(document.scripts || []).slice(0, 25).map((script) => script.src ? script.src : script.textContent?.slice(0, 80) || "inline");
332
+ let finalMessage = message;
333
+ if (error instanceof Response || message.includes("[object Response]") && error && typeof error === "object" && "status" in error) finalMessage = formatResponseError(error);
334
+ return {
335
+ message: finalMessage,
336
+ lineno,
337
+ colno,
338
+ filename: filename ?? void 0,
339
+ stack: error?.stack,
340
+ readyState: document.readyState,
341
+ userAgent: navigator.userAgent,
342
+ rootPresent: !!document.getElementById("root"),
343
+ scripts: scriptsSnapshot
344
+ };
345
+ }
346
+ function parseViteError({ message, lineno, colno, filename, stack, frame }) {
347
+ const scriptsSnapshot = Array.from(document.scripts || []).slice(0, 25).map((script) => script.src ? script.src : script.textContent?.slice(0, 80) || "inline");
348
+ let finalMessage = message;
349
+ if (frame) finalMessage = `${frame}\n${finalMessage}\n`;
350
+ return {
351
+ message: finalMessage,
352
+ lineno: lineno ?? 0,
353
+ colno: colno ?? 0,
354
+ filename: filename ?? void 0,
355
+ stack: stack ?? void 0,
356
+ readyState: document.readyState,
357
+ userAgent: navigator.userAgent,
358
+ rootPresent: !!document.getElementById("root"),
359
+ scripts: scriptsSnapshot
360
+ };
361
+ }
186
362
 
187
363
  //#endregion
188
- export { IframePostMessageClient, ViteHook, isNodeAttributesPatchBatchEvent, isNodeAttributesPatchEvent, isSetHoverNodeEvent, isSetSelectNodeEvent, isWorkspaceModeEvent };
364
+ export { IframePostMessageClient, ViteHook, extractResourceError, formatReason, getRuntimeFingerprint, hasMatchingStackFrame, isSetHoverNodeEvent, isSetSelectNodeEvent, isUpdateBatchEvent, isWorkspaceModeEvent, parseError, parseViteError, serialize };
@@ -1,5 +1,14 @@
1
- import { IframePostMessageClient, ViteHook } from "../client";
2
- import { ErrorInfo } from "src/client/iframe-post-message";
1
+ import {
2
+ serialize,
3
+ hasMatchingStackFrame,
4
+ formatReason,
5
+ IframePostMessageClient,
6
+ ViteHook,
7
+ extractResourceError,
8
+ parseError,
9
+ getRuntimeFingerprint,
10
+ parseViteError,
11
+ } from "../client";
3
12
 
4
13
  class ErrorTracker {
5
14
  public vHook: ViteHook;
@@ -23,54 +32,49 @@ class ErrorTracker {
23
32
  }
24
33
 
25
34
  formatConsoleMessage(args: unknown[]): string {
26
- let error;
27
- const texts = [];
28
- for (let arg of args) {
29
- if (arg instanceof Error) {
30
- error = arg;
31
- } else {
32
- texts.push(arg);
33
- }
34
- }
35
-
36
- if (error) {
37
- return error.stack || error.message;
38
- }
39
-
40
- return texts.map((text) => String(text)).join(" ");
35
+ let stack = null;
36
+ const err = new Error();
37
+ if (err.stack) stack = err.stack.split("\n").slice(2).join("\n");
38
+
39
+ const raw = args.map((a) => serialize(a));
40
+ const text = raw
41
+ .map((m) =>
42
+ typeof m === "string"
43
+ ? m
44
+ : JSON.stringify(m, null, 2)?.slice(0, 10_000),
45
+ )
46
+ .join(" ");
47
+
48
+ const message = (stack ? `${text}\n${stack}` : text).slice(0, 10_000);
49
+
50
+ return message;
41
51
  }
42
52
 
43
53
  handleConsoleInterception() {
44
- type LogLevel = keyof typeof originalConsole;
54
+ type level = keyof typeof original;
45
55
 
46
- const originalConsole = {
56
+ const original = {
47
57
  log: console.log,
48
58
  warn: console.warn,
49
59
  error: console.error,
50
60
  trace: console.trace,
51
- // debug: console.debug,
52
61
  };
53
- for (let logLevel of Object.keys(originalConsole) as LogLevel[]) {
54
- console[logLevel] = function (
55
- this: ErrorTracker,
56
- ...args: unknown[]
57
- ) {
58
- originalConsole[logLevel].apply(this, args);
62
+ for (let level of Object.keys(original) as level[]) {
63
+ console[level] = (...args: unknown[]) => {
64
+ original[level].apply(console, args);
59
65
 
60
66
  const message = this.formatConsoleMessage(args);
61
-
62
67
  if (!message) return;
63
-
64
- if (logLevel === "error") {
65
- console.debug("[vite-shaper] console error", message);
66
- this.iframeClient.sendError({
67
- errorType: "RUNTIME_ERROR",
68
+ if (level === "error" || level === "warn") {
69
+ this.iframeClient.sendConsoleMessage({
70
+ level,
71
+ message,
72
+ fingerprint: `${level}|${message}`,
68
73
  timestamp: Date.now(),
69
- stack: message,
70
- has_blank_screen: this.isBlankScreen(),
74
+ type: "CONSOLE",
71
75
  });
72
76
  }
73
- }.bind(this);
77
+ };
74
78
  }
75
79
  }
76
80
 
@@ -78,55 +82,111 @@ class ErrorTracker {
78
82
  this.vHook.onError((data) => {
79
83
  console.debug("[vite-shaper] vite:error", data);
80
84
 
81
- let error: ErrorInfo = {
82
- errorType: "RUNTIME_ERROR",
85
+ const message = data.err.message;
86
+ const filename = data.err.loc?.file;
87
+ const lineno = data.err.loc?.line;
88
+ const colno = data.err.loc?.column;
89
+ const stack = data.err.stack;
90
+ const frame = data.err.frame;
91
+
92
+ const dataError = parseViteError({
93
+ message,
94
+ filename,
95
+ lineno,
96
+ colno,
97
+ stack,
98
+ frame,
99
+ });
100
+
101
+ this.iframeClient.sendRuntimeError({
102
+ ...dataError,
103
+ hasBlankScreen: this.isBlankScreen(),
104
+ fingerprint: `${dataError.message}|${dataError.filename}|${dataError.lineno}|${dataError.colno}`,
105
+ type: "RUNTIME_ERROR",
83
106
  timestamp: Date.now(),
84
- stack: data.err.message,
85
- has_blank_screen: this.isBlankScreen(),
86
- };
87
- if (data.err.loc) {
88
- error = {
89
- ...error,
90
- lineno: data.err.loc.line,
91
- colno: data.err.loc.column,
92
- filename: data.err.loc.file,
93
- };
94
- }
95
- this.iframeClient.sendError(error);
107
+ });
96
108
  });
97
109
  }
98
110
 
99
111
  handleUnhandledrejection() {
100
- window.addEventListener("unhandledrejection", (evt) => {
101
- console.debug("[vite-shaper] handleUnhandledrejection", evt);
102
- this.iframeClient.sendError({
103
- errorType: "RUNTIME_ERROR",
112
+ window.addEventListener("unhandledrejection", (event) => {
113
+ const reason = event.reason;
114
+
115
+ const isResponseError = reason instanceof Response;
116
+ const shouldIgnore = hasMatchingStackFrame(reason);
117
+
118
+ if (!isResponseError && !reason?.stack && !shouldIgnore) return;
119
+
120
+ const fingerprint = isResponseError
121
+ ? `Response|${reason.status}|${reason.url}`
122
+ : reason?.stack || reason?.message || String(reason);
123
+
124
+ const message = isResponseError
125
+ ? formatReason(reason)
126
+ : reason?.message ||
127
+ String(reason || "Unhandled promise rejection");
128
+
129
+ const stack = isResponseError
130
+ ? `Response ${reason.status} at ${reason.url ?? "(unknown)"}`
131
+ : reason?.stack || String(reason);
132
+
133
+ const scripts = Array.from(document.scripts || [])
134
+ .slice(0, 25)
135
+ .map((script) => {
136
+ if (script.src) return script.src;
137
+ return script.textContent?.slice(0, 80) || "inline";
138
+ });
139
+
140
+ this.iframeClient.sendUnhandledRejection({
141
+ message,
142
+ stack,
143
+ readyState: document.readyState,
144
+ userAgent: navigator.userAgent,
145
+ rootPresent: !!document.getElementById("root"),
146
+ hasBlankScreen: this.isBlankScreen(),
147
+ scripts,
148
+ fingerprint,
104
149
  timestamp: Date.now(),
105
- stack: evt.reason.stack || evt.reason.message,
106
- has_blank_screen: this.isBlankScreen(),
150
+ type: "UNHANDLED_REJECTION",
107
151
  });
108
152
  });
109
153
  }
110
154
 
111
155
  handleError() {
112
- window.addEventListener("error", (evt) => {
113
- console.debug("[vite-shaper] handleError", evt);
114
- let message;
115
- if (evt.error) {
116
- message = evt.error.stack || evt.error.message;
117
- } else {
118
- message = evt.message;
119
- }
120
- this.iframeClient.sendError({
121
- errorType: "RUNTIME_ERROR",
122
- timestamp: Date.now(),
123
- stack: message,
124
- has_blank_screen: this.isBlankScreen(),
125
- lineno: evt.lineno,
126
- colno: evt.colno,
127
- filename: evt.filename,
128
- });
129
- });
156
+ window.addEventListener(
157
+ "error",
158
+ (event) => {
159
+ if (!(event instanceof ErrorEvent)) {
160
+ const resourceError = extractResourceError(event);
161
+
162
+ if (!resourceError) return;
163
+
164
+ const fingerprint = `resource|${resourceError.filename}`;
165
+
166
+ this.iframeClient.sendRuntimeError({
167
+ ...resourceError,
168
+ hasBlankScreen: this.isBlankScreen(),
169
+ fingerprint,
170
+ timestamp: Date.now(),
171
+ type: "RUNTIME_ERROR",
172
+ });
173
+
174
+ return;
175
+ }
176
+
177
+ const fingerprint = getRuntimeFingerprint(event);
178
+ const errorData = parseError(event);
179
+
180
+ this.iframeClient.sendRuntimeError({
181
+ ...errorData,
182
+ hasBlankScreen: this.isBlankScreen(),
183
+ fingerprint,
184
+ timestamp: Date.now(),
185
+ type: "RUNTIME_ERROR",
186
+ });
187
+ },
188
+ true,
189
+ );
130
190
  }
131
191
  }
132
192
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shaper.org/core",
3
- "version": "1.1.1",
3
+ "version": "1.2.0-0",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "private": false,