@lowdefy/client 0.0.0-experimental-20251203205559 → 0.0.0-experimental-20260112140412

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.
@@ -12,14 +12,40 @@
12
12
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
- */ import React, { useState } from 'react';
15
+ */ import React, { useEffect, useRef, useState } from 'react';
16
16
  import { ErrorBoundary } from '@lowdefy/block-utils';
17
17
  import CategorySwitch from './CategorySwitch.js';
18
18
  import MountEvents from '../MountEvents.js';
19
19
  const Block = ({ block, Blocks, context, lowdefy, parentLoading })=>{
20
20
  const [updates, setUpdate] = useState(0);
21
+ const loggedErrorsRef = useRef(new Set());
21
22
  lowdefy._internal.updaters[block.id] = ()=>setUpdate(updates + 1);
22
- return /*#__PURE__*/ React.createElement(ErrorBoundary, null, /*#__PURE__*/ React.createElement(MountEvents, {
23
+ const handleError = (error)=>{
24
+ if (lowdefy._internal.logError) {
25
+ lowdefy._internal.logError(error);
26
+ }
27
+ };
28
+ // Log parse errors to server
29
+ useEffect(()=>{
30
+ if (block.eval?.parseErrors && lowdefy._internal.logError) {
31
+ block.eval.parseErrors.forEach((error)=>{
32
+ // Use error message as key to avoid duplicate logs
33
+ const errorKey = `${block.id}:${error.message}`;
34
+ if (!loggedErrorsRef.current.has(errorKey)) {
35
+ loggedErrorsRef.current.add(errorKey);
36
+ lowdefy._internal.logError(error);
37
+ }
38
+ });
39
+ }
40
+ }, [
41
+ block.eval?.parseErrors,
42
+ block.id,
43
+ lowdefy._internal
44
+ ]);
45
+ return /*#__PURE__*/ React.createElement(ErrorBoundary, {
46
+ configKey: block.eval?.configKey,
47
+ onError: handleError
48
+ }, /*#__PURE__*/ React.createElement(MountEvents, {
23
49
  context: context,
24
50
  triggerEvent: async ()=>{
25
51
  context._internal.lowdefy._internal.progress.dispatch({
@@ -18,6 +18,73 @@ import createCallRequest from './createCallRequest.js';
18
18
  import createIcon from './createIcon.js';
19
19
  import createLinkComponent from './createLinkComponent.js';
20
20
  import setupLink from './setupLink.js';
21
+ function createLogError(lowdefy, windowObj) {
22
+ // Track logged errors for deduplication
23
+ const loggedErrors = new Set();
24
+ return async function logError(error) {
25
+ // Deduplicate by message + configKey
26
+ const errorKey = `${error.message}:${error.configKey || ''}`;
27
+ if (loggedErrors.has(errorKey)) {
28
+ return;
29
+ }
30
+ loggedErrors.add(errorKey);
31
+ const isServiceError = error.isServiceError === true;
32
+ const errorData = {
33
+ message: error.message,
34
+ name: error.name,
35
+ configKey: error.configKey,
36
+ isServiceError,
37
+ pageId: lowdefy.pageId,
38
+ timestamp: new Date().toISOString()
39
+ };
40
+ // Try to send to server first with 1s timeout (same-origin)
41
+ const controller = new AbortController();
42
+ const timeoutId = setTimeout(()=>controller.abort(), 1000);
43
+ try {
44
+ const response = await windowObj.fetch(`${lowdefy.basePath}/api/client-error`, {
45
+ method: 'POST',
46
+ headers: {
47
+ 'Content-Type': 'application/json'
48
+ },
49
+ body: JSON.stringify(errorData),
50
+ signal: controller.signal,
51
+ credentials: 'same-origin'
52
+ });
53
+ clearTimeout(timeoutId);
54
+ if (response.ok) {
55
+ const result = await response.json();
56
+ const errorType = result.isServiceError ? 'Service Error' : 'Config Error';
57
+ if (result.isServiceError) {
58
+ // Service errors don't need config location
59
+ console.error(`[${errorType}] ${error.message}`);
60
+ } else {
61
+ // Config errors show location info
62
+ let vscodeLink = '';
63
+ if (result.link) {
64
+ const match = result.link.match(/^(.+):(\d+)$/);
65
+ if (match) {
66
+ const [, filePath, line] = match;
67
+ vscodeLink = `vscode://file${filePath}?line=${line}`;
68
+ } else {
69
+ vscodeLink = `vscode://file${result.link}`;
70
+ }
71
+ }
72
+ const source = result.source ? `${result.source} at ${result.config}` : '';
73
+ console.error(`[${errorType}] ${error.message}\n ${source}\n ${vscodeLink}`);
74
+ }
75
+ } else {
76
+ // Server returned error - log locally as fallback
77
+ const errorType = isServiceError ? 'Service Error' : 'Config Error';
78
+ console.error(`[${errorType}] ${error.message}`);
79
+ }
80
+ } catch (fetchError) {
81
+ clearTimeout(timeoutId);
82
+ // Server unreachable or timeout - log locally as fallback
83
+ const errorType = isServiceError ? 'Service Error' : 'Config Error';
84
+ console.error(`[${errorType}] ${error.message}`);
85
+ }
86
+ };
87
+ }
21
88
  function initLowdefyContext({ auth, Components, config, lowdefy, router, stage, types, window }) {
22
89
  if (!lowdefy._internal?.initialised) {
23
90
  lowdefy._internal = {
@@ -58,6 +125,7 @@ function initLowdefyContext({ auth, Components, config, lowdefy, router, stage,
58
125
  lowdefy._internal.components.Link = createLinkComponent(lowdefy, Components.Link);
59
126
  lowdefy._internal.link = setupLink(lowdefy);
60
127
  lowdefy._internal.updateBlock = (blockId)=>lowdefy._internal.updaters[blockId] && lowdefy._internal.updaters[blockId]();
128
+ lowdefy._internal.logError = createLogError(lowdefy, window);
61
129
  if (stage === 'dev') {
62
130
  window.lowdefy = lowdefy;
63
131
  }
package/dist/request.js CHANGED
@@ -21,10 +21,8 @@
21
21
  body: JSON.stringify(body)
22
22
  });
23
23
  if (!res.ok) {
24
- // TODO: check
25
24
  const body = await res.json();
26
- console.log(res);
27
- console.log(body);
25
+ console.log('Error Response: ', res, body);
28
26
  throw new Error(body.message || 'Request error');
29
27
  }
30
28
  return res.json();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lowdefy/client",
3
- "version": "0.0.0-experimental-20251203205559",
3
+ "version": "0.0.0-experimental-20260112140412",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Lowdefy Client",
6
6
  "homepage": "https://lowdefy.com",
@@ -34,10 +34,10 @@
34
34
  ],
35
35
  "dependencies": {
36
36
  "@ant-design/icons": "4.8.0",
37
- "@lowdefy/block-utils": "0.0.0-experimental-20251203205559",
38
- "@lowdefy/engine": "0.0.0-experimental-20251203205559",
39
- "@lowdefy/helpers": "0.0.0-experimental-20251203205559",
40
- "@lowdefy/layout": "0.0.0-experimental-20251203205559",
37
+ "@lowdefy/block-utils": "0.0.0-experimental-20260112140412",
38
+ "@lowdefy/engine": "0.0.0-experimental-20260112140412",
39
+ "@lowdefy/helpers": "0.0.0-experimental-20260112140412",
40
+ "@lowdefy/layout": "0.0.0-experimental-20260112140412",
41
41
  "classnames": "2.3.2",
42
42
  "react": "18.2.0",
43
43
  "react-dom": "18.2.0"
@@ -45,7 +45,7 @@
45
45
  "devDependencies": {
46
46
  "@emotion/jest": "11.10.5",
47
47
  "@jest/globals": "28.1.3",
48
- "@lowdefy/jest-yaml-transform": "0.0.0-experimental-20251203205559",
48
+ "@lowdefy/jest-yaml-transform": "0.0.0-experimental-20260112140412",
49
49
  "@swc/cli": "0.1.63",
50
50
  "@swc/core": "1.3.99",
51
51
  "@swc/jest": "0.2.29",