@nocobase/flow-engine 2.2.0-alpha.1 → 2.2.0-alpha.10

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 (115) hide show
  1. package/lib/JSRunner.d.ts +1 -0
  2. package/lib/JSRunner.js +110 -20
  3. package/lib/acl/Acl.d.ts +2 -1
  4. package/lib/acl/Acl.js +28 -0
  5. package/lib/components/FieldModelRenderer.js +44 -31
  6. package/lib/components/FlowContextSelector.js +55 -12
  7. package/lib/components/FormItem.js +11 -7
  8. package/lib/components/MobilePopup.js +39 -10
  9. package/lib/components/MobilePopup.style.js +27 -6
  10. package/lib/components/dnd/index.js +9 -2
  11. package/lib/components/settings/wrappers/contextual/FlowsFloatContextMenu.js +86 -32
  12. package/lib/components/settings/wrappers/contextual/useFloatToolbarVisibility.js +20 -0
  13. package/lib/components/subModel/LazyDropdown.js +41 -26
  14. package/lib/components/variables/VariableHybridInput.d.ts +9 -0
  15. package/lib/components/variables/VariableHybridInput.js +146 -17
  16. package/lib/components/variables/VariableInput.js +19 -7
  17. package/lib/components/variables/VariableTag.js +48 -36
  18. package/lib/components/variables/types.d.ts +21 -0
  19. package/lib/flowContext.d.ts +1 -1
  20. package/lib/flowContext.js +97 -42
  21. package/lib/flowEngine.js +6 -0
  22. package/lib/flowI18n.js +3 -3
  23. package/lib/flowSettings.d.ts +5 -1
  24. package/lib/flowSettings.js +70 -0
  25. package/lib/locale/en-US.json +3 -0
  26. package/lib/locale/index.d.ts +6 -0
  27. package/lib/locale/zh-CN.json +3 -0
  28. package/lib/resources/apiResource.js +2 -1
  29. package/lib/resources/baseRecordResource.js +6 -17
  30. package/lib/resources/multiRecordResource.js +13 -3
  31. package/lib/resources/singleRecordResource.js +7 -2
  32. package/lib/runjs-context/helpers.js +12 -5
  33. package/lib/types.d.ts +15 -1
  34. package/lib/types.js +1 -0
  35. package/lib/utils/dataSourceDirty.d.ts +20 -0
  36. package/lib/utils/dataSourceDirty.js +139 -0
  37. package/lib/utils/dirtyAwareApiClient.d.ts +11 -0
  38. package/lib/utils/dirtyAwareApiClient.js +632 -0
  39. package/lib/utils/index.d.ts +1 -1
  40. package/lib/utils/index.js +11 -11
  41. package/lib/utils/loadedPageCache.d.ts +1 -0
  42. package/lib/utils/loadedPageCache.js +6 -0
  43. package/lib/utils/openViewRouteState.d.ts +28 -0
  44. package/lib/utils/openViewRouteState.js +125 -0
  45. package/lib/utils/parsePathnameToViewParams.d.ts +3 -0
  46. package/lib/utils/parsePathnameToViewParams.js +18 -1
  47. package/lib/utils/resolveRunJSObjectValues.js +3 -2
  48. package/lib/utils/runjsModuleLoader.js +0 -30
  49. package/lib/views/ViewNavigation.js +5 -0
  50. package/package.json +4 -4
  51. package/src/JSRunner.ts +112 -25
  52. package/src/__tests__/JSRunner.test.ts +4 -5
  53. package/src/__tests__/flowContext.test.ts +154 -0
  54. package/src/__tests__/flowEngine.dataSourceDirty.test.ts +51 -0
  55. package/src/__tests__/flowI18n.test.ts +11 -0
  56. package/src/__tests__/flowModel.openView.navigation.test.ts +28 -0
  57. package/src/__tests__/flowSettings.test.ts +72 -0
  58. package/src/__tests__/runjsRuntimeFeatures.test.ts +15 -2
  59. package/src/__tests__/viewScopedFlowEngine.test.ts +72 -6
  60. package/src/acl/Acl.tsx +36 -1
  61. package/src/acl/__tests__/Acl.test.tsx +70 -0
  62. package/src/components/FieldModelRenderer.tsx +50 -36
  63. package/src/components/FlowContextSelector.tsx +66 -11
  64. package/src/components/FormItem.tsx +12 -7
  65. package/src/components/MobilePopup.style.ts +34 -7
  66. package/src/components/MobilePopup.tsx +42 -10
  67. package/src/components/__tests__/FieldModelRenderer.test.tsx +165 -0
  68. package/src/components/__tests__/FormItem.test.tsx +17 -2
  69. package/src/components/__tests__/MobilePopup.style.test.tsx +103 -0
  70. package/src/components/__tests__/MobilePopup.test.tsx +150 -0
  71. package/src/components/dnd/index.tsx +11 -2
  72. package/src/components/settings/wrappers/contextual/FlowsFloatContextMenu.tsx +105 -35
  73. package/src/components/settings/wrappers/contextual/__tests__/FlowsFloatContextMenu.test.tsx +381 -12
  74. package/src/components/settings/wrappers/contextual/useFloatToolbarVisibility.ts +28 -0
  75. package/src/components/subModel/LazyDropdown.tsx +44 -26
  76. package/src/components/subModel/__tests__/AddSubModelButton.test.tsx +85 -2
  77. package/src/components/variables/VariableHybridInput.tsx +185 -14
  78. package/src/components/variables/VariableInput.tsx +32 -7
  79. package/src/components/variables/VariableTag.tsx +51 -37
  80. package/src/components/variables/__tests__/FlowContextSelector.test.tsx +60 -3
  81. package/src/components/variables/__tests__/VariableHybridInput.test.tsx +212 -0
  82. package/src/components/variables/__tests__/VariableInput.test.tsx +202 -6
  83. package/src/components/variables/__tests__/VariableTag.test.tsx +80 -0
  84. package/src/components/variables/types.ts +21 -0
  85. package/src/flowContext.ts +104 -36
  86. package/src/flowEngine.ts +6 -0
  87. package/src/flowI18n.ts +8 -3
  88. package/src/flowSettings.ts +85 -1
  89. package/src/locale/__tests__/index.test.ts +21 -0
  90. package/src/locale/en-US.json +3 -0
  91. package/src/locale/zh-CN.json +3 -0
  92. package/src/resources/apiResource.ts +2 -1
  93. package/src/resources/baseRecordResource.ts +6 -23
  94. package/src/resources/multiRecordResource.ts +13 -3
  95. package/src/resources/singleRecordResource.ts +6 -1
  96. package/src/runjs-context/helpers.ts +12 -6
  97. package/src/types.ts +16 -0
  98. package/src/utils/__tests__/dirtyAwareApiClient.test.ts +713 -0
  99. package/src/utils/__tests__/openViewRouteState.test.ts +40 -0
  100. package/src/utils/__tests__/parsePathnameToViewParams.test.ts +36 -0
  101. package/src/utils/dataSourceDirty.ts +126 -0
  102. package/src/utils/dirtyAwareApiClient.ts +742 -0
  103. package/src/utils/index.ts +10 -9
  104. package/src/utils/loadedPageCache.ts +7 -0
  105. package/src/utils/openViewRouteState.ts +107 -0
  106. package/src/utils/parsePathnameToViewParams.ts +23 -1
  107. package/src/utils/resolveRunJSObjectValues.ts +5 -2
  108. package/src/utils/runjsModuleLoader.ts +0 -32
  109. package/src/views/ViewNavigation.ts +6 -1
  110. package/src/views/__tests__/ViewNavigation.test.ts +15 -0
  111. package/lib/utils/safeGlobals.d.ts +0 -28
  112. package/lib/utils/safeGlobals.js +0 -367
  113. package/src/utils/__tests__/runjsRequireAsyncAutoWhitelist.test.ts +0 -38
  114. package/src/utils/__tests__/safeGlobals.test.ts +0 -106
  115. package/src/utils/safeGlobals.ts +0 -406
@@ -66,6 +66,16 @@ export { extractPropertyPath, formatPathToVariable, isVariableExpression } from
66
66
 
67
67
  export { clearAutoFlowError, getAutoFlowError, setAutoFlowError, type AutoFlowError } from './autoFlowError';
68
68
  export { parsePathnameToViewParams, type ViewParam } from './parsePathnameToViewParams';
69
+ export {
70
+ createOpenViewRouteState,
71
+ decodeOpenViewRouteState,
72
+ encodeOpenViewRouteState,
73
+ isOpenViewRouteStateToken,
74
+ RUNJS_OPEN_VIEW_ROUTE_STATE,
75
+ type OpenViewRouteMode,
76
+ type OpenViewRouteSize,
77
+ type OpenViewRouteState,
78
+ } from './openViewRouteState';
69
79
  export {
70
80
  decodeBase64Url,
71
81
  encodeBase64Url,
@@ -77,15 +87,6 @@ export {
77
87
  serializeCtxDateValue,
78
88
  } from './dateVariable';
79
89
 
80
- // 安全全局对象(window/document)
81
- export {
82
- createSafeDocument,
83
- createSafeWindow,
84
- createSafeNavigator,
85
- createSafeRunJSGlobals,
86
- runjsWithSafeGlobals,
87
- } from './safeGlobals';
88
-
89
90
  // RunJS value helpers
90
91
  export { isRunJSValue, normalizeRunJSValue, extractUsedVariablePathsFromRunJS, type RunJSValue } from './runjsValue';
91
92
 
@@ -123,6 +123,13 @@ export const createLoadedPageCache = () => {
123
123
  }
124
124
  },
125
125
 
126
+ markDirtyForOptions(options?: LoadedPageOptions): void {
127
+ const key = getLoadedPageKey(options);
128
+ if (key) {
129
+ dirtyKeys.add(key);
130
+ }
131
+ },
132
+
126
133
  shouldBypass(options?: LoadedPageOptions, isFlowSettingsEnabled?: () => boolean): boolean {
127
134
  const key = getLoadedPageKey(options);
128
135
  if (!key || !dirtyKeys.has(key)) {
@@ -0,0 +1,107 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ const OPEN_VIEW_ROUTE_MODES = ['drawer', 'dialog', 'embed'] as const;
11
+ const OPEN_VIEW_ROUTE_SIZES = ['small', 'medium', 'large'] as const;
12
+ const OPEN_VIEW_ROUTE_STATE_TOKEN_ALPHABET = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
13
+ const OPEN_VIEW_ROUTE_STATE_TOKEN_LENGTH = 8;
14
+
15
+ export type OpenViewRouteMode = (typeof OPEN_VIEW_ROUTE_MODES)[number];
16
+ export type OpenViewRouteSize = (typeof OPEN_VIEW_ROUTE_SIZES)[number];
17
+
18
+ export type OpenViewRouteState = {
19
+ mode?: OpenViewRouteMode;
20
+ size?: OpenViewRouteSize;
21
+ };
22
+
23
+ export const RUNJS_OPEN_VIEW_ROUTE_STATE = Symbol.for('nocobase.runjs.openViewRouteState');
24
+
25
+ function isOpenViewRouteMode(value: unknown): value is OpenViewRouteMode {
26
+ return typeof value === 'string' && (OPEN_VIEW_ROUTE_MODES as readonly string[]).includes(value);
27
+ }
28
+
29
+ function isOpenViewRouteSize(value: unknown): value is OpenViewRouteSize {
30
+ return typeof value === 'string' && (OPEN_VIEW_ROUTE_SIZES as readonly string[]).includes(value);
31
+ }
32
+
33
+ export function createOpenViewRouteState(input?: { mode?: unknown; size?: unknown }): OpenViewRouteState | undefined {
34
+ const state: OpenViewRouteState = {};
35
+
36
+ if (isOpenViewRouteMode(input?.mode)) {
37
+ state.mode = input.mode;
38
+ }
39
+ if (isOpenViewRouteSize(input?.size)) {
40
+ state.size = input.size;
41
+ }
42
+
43
+ return state.mode || state.size ? state : undefined;
44
+ }
45
+
46
+ function hashString(value: string) {
47
+ let hash = 2166136261;
48
+ for (let i = 0; i < value.length; i++) {
49
+ hash ^= value.charCodeAt(i);
50
+ hash = Math.imul(hash, 16777619);
51
+ }
52
+ return hash >>> 0;
53
+ }
54
+
55
+ function stateToCode(state: OpenViewRouteState) {
56
+ const modeIndex = state.mode ? OPEN_VIEW_ROUTE_MODES.indexOf(state.mode) + 1 : 0;
57
+ const sizeIndex = state.size ? OPEN_VIEW_ROUTE_SIZES.indexOf(state.size) + 1 : 0;
58
+ const code = modeIndex * 4 + sizeIndex;
59
+ return code > 0 ? code : undefined;
60
+ }
61
+
62
+ function codeToState(code: number): OpenViewRouteState | undefined {
63
+ const modeIndex = Math.floor(code / 4);
64
+ const sizeIndex = code % 4;
65
+ return createOpenViewRouteState({
66
+ mode: modeIndex ? OPEN_VIEW_ROUTE_MODES[modeIndex - 1] : undefined,
67
+ size: sizeIndex ? OPEN_VIEW_ROUTE_SIZES[sizeIndex - 1] : undefined,
68
+ });
69
+ }
70
+
71
+ function tokenForCode(viewUid: string, code: number) {
72
+ let seed = hashString(`${viewUid}:${code}`);
73
+ let token = '';
74
+ for (let i = 0; i < OPEN_VIEW_ROUTE_STATE_TOKEN_LENGTH; i++) {
75
+ seed = Math.imul(seed ^ (code + i * 17), 16777619) >>> 0;
76
+ token += OPEN_VIEW_ROUTE_STATE_TOKEN_ALPHABET[seed % OPEN_VIEW_ROUTE_STATE_TOKEN_ALPHABET.length];
77
+ }
78
+ return token;
79
+ }
80
+
81
+ export function isOpenViewRouteStateToken(value: unknown): value is string {
82
+ return (
83
+ typeof value === 'string' &&
84
+ value.length === OPEN_VIEW_ROUTE_STATE_TOKEN_LENGTH &&
85
+ [...value].every((char) => OPEN_VIEW_ROUTE_STATE_TOKEN_ALPHABET.includes(char))
86
+ );
87
+ }
88
+
89
+ export function encodeOpenViewRouteState(viewUid: string, input?: { mode?: unknown; size?: unknown }) {
90
+ const state = createOpenViewRouteState(input);
91
+ const code = state ? stateToCode(state) : undefined;
92
+ return code ? tokenForCode(viewUid, code) : undefined;
93
+ }
94
+
95
+ export function decodeOpenViewRouteState(viewUid: string, token: unknown) {
96
+ if (!isOpenViewRouteStateToken(token)) {
97
+ return undefined;
98
+ }
99
+
100
+ for (let code = 1; code < 16; code++) {
101
+ if (tokenForCode(viewUid, code) === token) {
102
+ return codeToState(code);
103
+ }
104
+ }
105
+
106
+ return undefined;
107
+ }
@@ -7,6 +7,8 @@
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
9
 
10
+ import { decodeOpenViewRouteState, type OpenViewRouteState } from './openViewRouteState';
11
+
10
12
  export interface ViewParam {
11
13
  /** 视图唯一标识符,一般为某个 Model 实例的 uid */
12
14
  viewUid: string;
@@ -16,6 +18,8 @@ export interface ViewParam {
16
18
  filterByTk?: string | Record<string, string | number>;
17
19
  /** source Id */
18
20
  sourceId?: string;
21
+ /** RunJS ctx.openView runtime display overrides decoded from URL. */
22
+ openViewRouteState?: OpenViewRouteState;
19
23
  }
20
24
 
21
25
  export interface ParsePathnameToViewParamsOptions {
@@ -109,7 +113,25 @@ export const parsePathnameToViewParams = (
109
113
  }
110
114
  }
111
115
  // 处理参数
112
- else if (currentView && i + 1 < segments.length) {
116
+ else if (currentView) {
117
+ if (segment === 'opts') {
118
+ if (i + 1 < segments.length) {
119
+ const routeState = decodeOpenViewRouteState(currentView.viewUid, segments[i + 1]);
120
+ if (routeState) {
121
+ currentView.openViewRouteState = routeState;
122
+ }
123
+ i += 2;
124
+ } else {
125
+ i++;
126
+ }
127
+ continue;
128
+ }
129
+
130
+ if (i + 1 >= segments.length) {
131
+ i++;
132
+ continue;
133
+ }
134
+
113
135
  const rawValue = segments[i + 1];
114
136
  // 尝试对路径段进行解码
115
137
  let decoded: string = rawValue;
@@ -8,7 +8,6 @@
8
8
  */
9
9
 
10
10
  import { isRunJSValue, normalizeRunJSValue } from './runjsValue';
11
- import { runjsWithSafeGlobals } from './safeGlobals';
12
11
 
13
12
  /**
14
13
  * Resolve an object's values, executing any RunJSValue entries via ctx.runjs.
@@ -29,7 +28,11 @@ export async function resolveRunJSObjectValues(ctx: unknown, raw: unknown): Prom
29
28
  if (isRunJSValue(value)) {
30
29
  const { code, version } = normalizeRunJSValue(value);
31
30
  if (!code.trim()) continue;
32
- const ret = await runjsWithSafeGlobals(ctx, code, { version });
31
+ const runjsCtx = ctx as
32
+ | { runjs?: (code: string, variables?: Record<string, any>, options?: Record<string, any>) => Promise<any> }
33
+ | undefined
34
+ | null;
35
+ const ret = await runjsCtx?.runjs?.(code, undefined, { version });
33
36
  if (!ret?.success) {
34
37
  throw new Error(`RunJS execution failed for "${key}"`);
35
38
  }
@@ -9,7 +9,6 @@
9
9
 
10
10
  import { setRunJSLibOverride } from '../runjsLibs';
11
11
  import { resolveModuleUrl } from './resolveModuleUrl';
12
- import { registerRunJSSafeDocumentGlobals, registerRunJSSafeWindowGlobals } from './safeGlobals';
13
12
 
14
13
  /**
15
14
  * RunJS 外部模块加载辅助(浏览器侧)。
@@ -38,26 +37,6 @@ type ParsedPackageSpecifier = {
38
37
  subpath?: string;
39
38
  };
40
39
 
41
- function snapshotOwnKeys(obj: any): string[] {
42
- try {
43
- if (!obj || (typeof obj !== 'object' && typeof obj !== 'function')) return [];
44
- return Object.getOwnPropertyNames(obj);
45
- } catch (_) {
46
- return [];
47
- }
48
- }
49
-
50
- function diffAddedKeys(afterKeys: string[], beforeKeys: string[]): string[] {
51
- if (!afterKeys.length) return [];
52
- if (!beforeKeys.length) return [...afterKeys];
53
- const beforeSet = new Set(beforeKeys);
54
- const added: string[] = [];
55
- for (const k of afterKeys) {
56
- if (!beforeSet.has(k)) added.push(k);
57
- }
58
- return added;
59
- }
60
-
61
40
  /**
62
41
  * 使用全局 Promise 链实现“互斥锁”:
63
42
  * - 锁存放在 `globalThis.__nocobaseRunjsModuleLoadLock`;
@@ -298,9 +277,6 @@ async function prefetchEsmModule(url: string, options?: { timeoutMs?: number }):
298
277
  */
299
278
  export async function runjsRequireAsync(requirejs: RequireJsLike, url: string): Promise<any> {
300
279
  return await withRunjsModuleLoadLock(async () => {
301
- const beforeWinKeys = typeof window !== 'undefined' ? snapshotOwnKeys(window) : [];
302
- const beforeDocKeys = typeof document !== 'undefined' ? snapshotOwnKeys(document) : [];
303
-
304
280
  let result: any;
305
281
  let error: any;
306
282
  try {
@@ -319,14 +295,6 @@ export async function runjsRequireAsync(requirejs: RequireJsLike, url: string):
319
295
  });
320
296
  } catch (e) {
321
297
  error = e;
322
- } finally {
323
- const afterWinKeys = typeof window !== 'undefined' ? snapshotOwnKeys(window) : [];
324
- const afterDocKeys = typeof document !== 'undefined' ? snapshotOwnKeys(document) : [];
325
- const addedWinKeys = diffAddedKeys(afterWinKeys, beforeWinKeys);
326
- const addedDocKeys = diffAddedKeys(afterDocKeys, beforeDocKeys);
327
- // Best-effort: allow RunJS safe window/document to access globals introduced by this module load.
328
- registerRunJSSafeWindowGlobals(addedWinKeys);
329
- registerRunJSSafeDocumentGlobals(addedDocKeys);
330
298
  }
331
299
 
332
300
  if (error) throw error;
@@ -9,7 +9,7 @@
9
9
 
10
10
  import { FlowEngineContext } from '../flowContext';
11
11
  import { define, observable } from '../reactive';
12
- import { ViewParam as SharedViewParam } from '../utils';
12
+ import { encodeOpenViewRouteState, ViewParam as SharedViewParam } from '../utils';
13
13
 
14
14
  type ViewParams = Omit<SharedViewParam, 'viewUid'> & { viewUid?: string };
15
15
 
@@ -79,6 +79,11 @@ export function generatePathnameFromViewParams(
79
79
  // 添加视图 UID
80
80
  segments.push(viewParam.viewUid);
81
81
 
82
+ const openViewRouteStateToken = encodeOpenViewRouteState(viewParam.viewUid, viewParam.openViewRouteState);
83
+ if (openViewRouteStateToken) {
84
+ segments.push('opts', openViewRouteStateToken);
85
+ }
86
+
82
87
  // 添加参数
83
88
  if (viewParam.tabUid) {
84
89
  segments.push('tab', viewParam.tabUid);
@@ -7,6 +7,7 @@
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
9
 
10
+ import { encodeOpenViewRouteState } from '../../utils/openViewRouteState';
10
11
  import { ViewNavigation, generatePathnameFromViewParams } from '../ViewNavigation';
11
12
 
12
13
  describe('ViewNavigation', () => {
@@ -220,6 +221,20 @@ describe('generatePathnameFromViewParams', () => {
220
221
  expect(generatePathnameFromViewParams([{ viewUid: 'xxx' }, { viewUid: 'yyy' }])).toBe('/admin/xxx/view/yyy');
221
222
  });
222
223
 
224
+ it('should generate RunJS openView route state params after the matching view uid', () => {
225
+ const token = encodeOpenViewRouteState('yyy', { mode: 'dialog', size: 'large' });
226
+ if (!token) {
227
+ throw new Error('Expected openView route state token.');
228
+ }
229
+ const pathname = generatePathnameFromViewParams([
230
+ { viewUid: 'xxx' },
231
+ { viewUid: 'yyy', openViewRouteState: { mode: 'dialog', size: 'large' }, filterByTk: '1' },
232
+ ]);
233
+
234
+ expect(token).toMatch(/^[A-Za-z]{8}$/);
235
+ expect(pathname).toBe(`/admin/xxx/view/yyy/opts/${token}/filterbytk/1`);
236
+ });
237
+
223
238
  it('should generate complex path with all parameters', () => {
224
239
  expect(
225
240
  generatePathnameFromViewParams([
@@ -1,28 +0,0 @@
1
- /**
2
- * This file is part of the NocoBase (R) project.
3
- * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
- * Authors: NocoBase Team.
5
- *
6
- * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
- * For more information, please refer to: https://www.nocobase.com/agreement.
8
- */
9
- export declare function registerRunJSSafeWindowGlobals(keys: Iterable<string> | null | undefined): void;
10
- export declare function registerRunJSSafeDocumentGlobals(keys: Iterable<string> | null | undefined): void;
11
- export declare function __resetRunJSSafeGlobalsRegistryForTests(): void;
12
- export declare function createSafeWindow(extra?: Record<string, any>): Record<string, any>;
13
- export declare function createSafeDocument(extra?: Record<string, any>): Record<string, any>;
14
- export declare function createSafeNavigator(extra?: Record<string, any>): {};
15
- /**
16
- * Create a safe globals object for RunJS execution.
17
- *
18
- * - Always tries to provide `navigator`
19
- * - Best-effort provides `window` and `document` in browser environments
20
- * - Never throws (so callers can decide how to handle missing globals)
21
- */
22
- export declare function createSafeRunJSGlobals(extraGlobals?: Record<string, any>): Record<string, any>;
23
- /**
24
- * Execute RunJS with safe globals (window/document/navigator).
25
- *
26
- * Keeps `this` binding by calling `ctx.runjs(...)` instead of passing bare function references.
27
- */
28
- export declare function runjsWithSafeGlobals(ctx: unknown, code: string, options?: any, extraGlobals?: Record<string, any>): Promise<any>;