@nocobase/client-v2 2.1.11 → 2.2.0-alpha.2
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/es/PluginSettingsManager.d.ts +33 -0
- package/es/RouteRepository.d.ts +21 -6
- package/es/components/category-tabs/SortableCategoryTabs.d.ts +55 -0
- package/es/components/category-tabs/index.d.ts +9 -0
- package/es/components/form/TypedVariableInput.d.ts +22 -4
- package/es/components/form/filter/CollectionFilterItem.d.ts +11 -1
- package/es/components/form/filter/index.d.ts +2 -0
- package/es/components/index.d.ts +2 -0
- package/es/flow/actions/afterSuccess.d.ts +8 -1
- package/es/flow/actions/index.d.ts +1 -1
- package/es/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.d.ts +2 -0
- package/es/flow/admin-shell/admin-layout/AdminLayoutMenuFlowUtils.d.ts +2 -1
- package/es/flow/admin-shell/admin-layout/AdminLayoutMenuModels.d.ts +2 -0
- package/es/flow/admin-shell/admin-layout/AdminLayoutMenuUtils.d.ts +3 -2
- package/es/flow/admin-shell/admin-layout/AdminLayoutSlotModels.d.ts +5 -0
- package/es/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.d.ts +6 -0
- package/es/flow/components/FieldAssignValueInput.d.ts +2 -0
- package/es/flow/index.d.ts +1 -0
- package/es/flow/models/base/GridModel.d.ts +1 -1
- package/es/flow/models/blocks/form/submitHandler.d.ts +1 -1
- package/es/flow/models/blocks/table/TableActionsColumnModel.d.ts +1 -0
- package/es/flow/models/blocks/table/TableBlockModel.d.ts +1 -0
- package/es/flow/models/blocks/table/dragSort/dragSortHooks.d.ts +1 -1
- package/es/flow/models/blocks/table/dragSort/dragSortSettings.d.ts +2 -1
- package/es/flow/models/blocks/table/dragSort/dragSortUtils.d.ts +6 -4
- package/es/flow/resolveViewParamsToViewList.d.ts +1 -1
- package/es/index.d.ts +2 -1
- package/es/index.mjs +181 -131
- package/lib/index.js +186 -136
- package/package.json +7 -7
- package/src/PluginSettingsManager.ts +53 -0
- package/src/RouteRepository.ts +126 -24
- package/src/__tests__/PluginSettingsManager.test.ts +13 -0
- package/src/__tests__/RouteRepository.test.ts +216 -0
- package/src/__tests__/browserChecker.test.ts +61 -0
- package/src/__tests__/exports.test.ts +16 -0
- package/src/__tests__/plugin-manager.test.tsx +44 -2
- package/src/__tests__/settings-center.test.tsx +40 -1
- package/src/collection-manager/field-configure.ts +1 -1
- package/src/collection-manager/interfaces/id.ts +1 -1
- package/src/collection-manager/interfaces/m2m.tsx +2 -2
- package/src/collection-manager/interfaces/m2o.tsx +2 -2
- package/src/collection-manager/interfaces/nanoid.ts +1 -1
- package/src/collection-manager/interfaces/o2m.tsx +2 -2
- package/src/collection-manager/interfaces/obo.tsx +2 -2
- package/src/collection-manager/interfaces/oho.tsx +2 -2
- package/src/collection-manager/interfaces/properties/index.ts +2 -2
- package/src/collection-manager/interfaces/uuid.ts +1 -1
- package/src/components/README.md +7 -1
- package/src/components/README.zh-CN.md +6 -1
- package/src/components/category-tabs/SortableCategoryTabs.tsx +210 -0
- package/src/components/category-tabs/index.ts +10 -0
- package/src/components/form/TypedVariableInput.tsx +416 -93
- package/src/components/form/__tests__/TypedVariableInput.test.tsx +266 -9
- package/src/components/form/filter/CollectionFilterItem.tsx +32 -7
- package/src/components/form/filter/__tests__/CollectionFilterItem.test.tsx +38 -0
- package/src/components/form/filter/index.ts +2 -0
- package/src/components/index.ts +2 -0
- package/src/flow/__tests__/FlowRoute.test.tsx +443 -5
- package/src/flow/__tests__/getKey.test.ts +7 -0
- package/src/flow/__tests__/resolveViewParamsToViewList.test.ts +34 -0
- package/src/flow/actions/__tests__/afterSuccess.test.ts +73 -0
- package/src/flow/actions/__tests__/openView.defineProps.route.test.tsx +104 -0
- package/src/flow/actions/afterSuccess.tsx +142 -3
- package/src/flow/actions/customVariable.tsx +1 -2
- package/src/flow/actions/index.ts +1 -1
- package/src/flow/actions/linkageRules.tsx +2 -7
- package/src/flow/actions/openView.tsx +38 -4
- package/src/flow/actions/runjs.tsx +2 -14
- package/src/flow/admin-shell/BaseLayoutModel.tsx +25 -3
- package/src/flow/admin-shell/BaseLayoutRouteCoordinator.ts +5 -2
- package/src/flow/admin-shell/__tests__/AdminLayoutRouteCoordinator.test.ts +13 -0
- package/src/flow/admin-shell/admin-layout/AdminLayoutComponent.tsx +34 -10
- package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.test.tsx +310 -0
- package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.tsx +44 -8
- package/src/flow/admin-shell/admin-layout/AdminLayoutMenuFlowUtils.ts +5 -3
- package/src/flow/admin-shell/admin-layout/AdminLayoutMenuModels.tsx +61 -9
- package/src/flow/admin-shell/admin-layout/AdminLayoutMenuUtils.tsx +71 -8
- package/src/flow/admin-shell/admin-layout/AdminLayoutModel.tsx +1 -1
- package/src/flow/admin-shell/admin-layout/AdminLayoutSlotModels.tsx +23 -9
- package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutComponent.test.tsx +188 -0
- package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutMenuModels.test.ts +99 -0
- package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutModel.test.tsx +82 -0
- package/src/flow/admin-shell/admin-layout/__tests__/TopbarActionsBar.test.tsx +166 -2
- package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.test.ts +20 -0
- package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.ts +33 -5
- package/src/flow/components/DefaultValue.tsx +1 -2
- package/src/flow/components/FieldAssignValueInput.tsx +10 -5
- package/src/flow/components/FlowRoute.tsx +56 -11
- package/src/flow/components/code-editor/__tests__/useCodeRunner.test.tsx +2 -17
- package/src/flow/components/code-editor/hooks/useCodeRunner.ts +2 -14
- package/src/flow/components/code-editor/runjsDiagnostics.ts +8 -45
- package/src/flow/getViewDiffAndUpdateHidden.tsx +1 -0
- package/src/flow/index.ts +1 -0
- package/src/flow/models/actions/JSActionModel.tsx +2 -7
- package/src/flow/models/actions/JSCollectionActionModel.tsx +2 -7
- package/src/flow/models/actions/JSItemActionModel.tsx +2 -7
- package/src/flow/models/actions/JSRecordActionModel.tsx +2 -7
- package/src/flow/models/base/ActionModel.tsx +12 -1
- package/src/flow/models/base/GridModel.tsx +38 -7
- package/src/flow/models/base/PageModel/__tests__/PageModel.test.ts +0 -4
- package/src/flow/models/base/__tests__/ActionModel.test.ts +83 -0
- package/src/flow/models/base/__tests__/GridModel.dragSnapshotContainer.test.ts +239 -1
- package/src/flow/models/base/__tests__/transformRowsToSingleColumn.test.ts +48 -0
- package/src/flow/models/blocks/filter-form/FilterFormBlockModel.tsx +1 -2
- package/src/flow/models/blocks/filter-form/FilterFormJSActionModel.tsx +2 -7
- package/src/flow/models/blocks/filter-form/fields/FilterFormCustomFieldModel.tsx +1 -1
- package/src/flow/models/blocks/form/FormActionModel.tsx +1 -1
- package/src/flow/models/blocks/form/JSFormActionModel.tsx +2 -7
- package/src/flow/models/blocks/form/__tests__/submitHandler.test.ts +54 -1
- package/src/flow/models/blocks/form/submitHandler.ts +17 -3
- package/src/flow/models/blocks/form/value-runtime/rules.ts +2 -2
- package/src/flow/models/blocks/js-block/JSBlock.tsx +2 -7
- package/src/flow/models/blocks/table/JSColumnModel.tsx +1 -9
- package/src/flow/models/blocks/table/TableActionsColumnModel.tsx +36 -13
- package/src/flow/models/blocks/table/TableBlockModel.tsx +38 -17
- package/src/flow/models/blocks/table/__tests__/TableActionsColumnModel.test.tsx +111 -1
- package/src/flow/models/blocks/table/__tests__/TableBlockModel.dragSort.test.ts +127 -0
- package/src/flow/models/blocks/table/__tests__/TableBlockModel.mobileSettingsButtons.test.tsx +78 -0
- package/src/flow/models/blocks/table/__tests__/TableBlockModel.rowSelection.test.tsx +1 -0
- package/src/flow/models/blocks/table/dragSort/dragSortHooks.tsx +28 -17
- package/src/flow/models/blocks/table/dragSort/dragSortSettings.ts +13 -6
- package/src/flow/models/blocks/table/dragSort/dragSortUtils.ts +15 -2
- package/src/flow/models/fields/JSEditableFieldModel.tsx +2 -11
- package/src/flow/models/fields/JSFieldModel.tsx +2 -7
- package/src/flow/models/fields/JSItemModel.tsx +2 -14
- package/src/flow/models/topbar/TopbarActionModel.tsx +93 -10
- package/src/flow/resolveViewParamsToViewList.tsx +11 -3
- package/src/flow-compat/Popover.tsx +43 -4
- package/src/flow-compat/__tests__/Popover.test.tsx +34 -0
- package/src/index.ts +8 -1
- package/src/layout-manager/__tests__/LayoutRoute.test.tsx +41 -1
- package/src/nocobase-buildin-plugin/index.tsx +0 -2
- package/src/settings-center/AdminSettingsLayout.tsx +10 -2
- package/src/settings-center/SystemSettingsPage.tsx +1 -2
- package/src/settings-center/plugin-manager/PluginCard.tsx +2 -2
- package/src/settings-center/utils.tsx +0 -6
|
@@ -8,12 +8,20 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import React from 'react';
|
|
11
|
-
import { render, screen } from '@testing-library/react';
|
|
11
|
+
import { fireEvent, render, screen } from '@testing-library/react';
|
|
12
|
+
import { MemoryRouter } from 'react-router-dom';
|
|
12
13
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
13
14
|
|
|
14
|
-
const { allowMock, flowModelRendererSpy } = vi.hoisted(() => {
|
|
15
|
+
const { allowMock, appMock, flowModelRendererSpy } = vi.hoisted(() => {
|
|
15
16
|
return {
|
|
16
17
|
allowMock: vi.fn(),
|
|
18
|
+
appMock: {
|
|
19
|
+
current: {
|
|
20
|
+
router: {
|
|
21
|
+
getBasename: () => '/nocobase/v',
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
},
|
|
17
25
|
flowModelRendererSpy: vi.fn(),
|
|
18
26
|
};
|
|
19
27
|
});
|
|
@@ -32,6 +40,12 @@ vi.mock('../HelpLite', () => {
|
|
|
32
40
|
};
|
|
33
41
|
});
|
|
34
42
|
|
|
43
|
+
vi.mock('../../../../hooks/useApp', () => {
|
|
44
|
+
return {
|
|
45
|
+
useApp: () => appMock.current,
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
|
|
35
49
|
vi.mock('@nocobase/flow-engine', async (importOriginal) => {
|
|
36
50
|
const actual = await importOriginal<typeof import('@nocobase/flow-engine')>();
|
|
37
51
|
return {
|
|
@@ -63,10 +77,19 @@ const createAction = (options: Record<string, any>) => {
|
|
|
63
77
|
} as any;
|
|
64
78
|
};
|
|
65
79
|
|
|
80
|
+
const renderSettingsLabel = (label: React.ReactNode, pathname: string) => {
|
|
81
|
+
return render(<MemoryRouter initialEntries={[pathname]}>{label}</MemoryRouter>);
|
|
82
|
+
};
|
|
83
|
+
|
|
66
84
|
describe('TopbarActionsBar helpers', () => {
|
|
67
85
|
beforeEach(() => {
|
|
68
86
|
allowMock.mockReset();
|
|
69
87
|
flowModelRendererSpy.mockClear();
|
|
88
|
+
appMock.current = {
|
|
89
|
+
router: {
|
|
90
|
+
getBasename: () => '/nocobase/v',
|
|
91
|
+
},
|
|
92
|
+
};
|
|
70
93
|
});
|
|
71
94
|
|
|
72
95
|
afterEach(() => {
|
|
@@ -149,6 +172,147 @@ describe('TopbarActionsBar helpers', () => {
|
|
|
149
172
|
});
|
|
150
173
|
});
|
|
151
174
|
|
|
175
|
+
it('should open regular admin settings in a new tab outside admin runtime', () => {
|
|
176
|
+
const items = getTopbarPluginSettingsItems({
|
|
177
|
+
canManagePlugins: false,
|
|
178
|
+
t: (key) => key,
|
|
179
|
+
settings: [
|
|
180
|
+
{
|
|
181
|
+
key: 'system-settings',
|
|
182
|
+
name: 'system-settings',
|
|
183
|
+
title: 'System settings',
|
|
184
|
+
path: '/admin/settings/system-settings',
|
|
185
|
+
icon: null,
|
|
186
|
+
componentLoader: async () => null,
|
|
187
|
+
},
|
|
188
|
+
] as any,
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
renderSettingsLabel((items as any[])[0].label, '/sales/p1');
|
|
192
|
+
|
|
193
|
+
const link = screen.getByRole('link', { name: 'System settings' });
|
|
194
|
+
expect(link).toHaveAttribute('href', '/nocobase/v/admin/settings/system-settings');
|
|
195
|
+
expect(link).toHaveAttribute('target', '_blank');
|
|
196
|
+
expect(link).toHaveAttribute('rel', expect.stringContaining('noopener'));
|
|
197
|
+
expect(link).toHaveAttribute('rel', expect.stringContaining('noreferrer'));
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it('should open plugin manager in a new tab outside admin runtime', () => {
|
|
201
|
+
const items = getTopbarPluginSettingsItems({
|
|
202
|
+
canManagePlugins: true,
|
|
203
|
+
t: (key) => key,
|
|
204
|
+
settings: [
|
|
205
|
+
{
|
|
206
|
+
key: 'plugin-manager',
|
|
207
|
+
name: 'plugin-manager',
|
|
208
|
+
title: 'Plugin manager',
|
|
209
|
+
path: '/admin/settings/plugin-manager',
|
|
210
|
+
icon: null,
|
|
211
|
+
componentLoader: async () => null,
|
|
212
|
+
},
|
|
213
|
+
] as any,
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
renderSettingsLabel((items as any[])[0].label, '/sales/p1');
|
|
217
|
+
|
|
218
|
+
const link = screen.getByRole('link', { name: 'Plugin manager' });
|
|
219
|
+
expect(link).toHaveAttribute('href', '/nocobase/v/admin/settings/plugin-manager');
|
|
220
|
+
expect(link).toHaveAttribute('target', '_blank');
|
|
221
|
+
expect(link).toHaveAttribute('rel', expect.stringContaining('noopener'));
|
|
222
|
+
expect(link).toHaveAttribute('rel', expect.stringContaining('noreferrer'));
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('should keep regular admin settings as SPA links inside admin runtime', () => {
|
|
226
|
+
const items = getTopbarPluginSettingsItems({
|
|
227
|
+
canManagePlugins: false,
|
|
228
|
+
t: (key) => key,
|
|
229
|
+
settings: [
|
|
230
|
+
{
|
|
231
|
+
key: 'routes',
|
|
232
|
+
name: 'routes',
|
|
233
|
+
title: 'Routes',
|
|
234
|
+
path: '/admin/settings/routes',
|
|
235
|
+
icon: null,
|
|
236
|
+
componentLoader: async () => null,
|
|
237
|
+
},
|
|
238
|
+
] as any,
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
renderSettingsLabel((items as any[])[0].label, '/admin/settings/routes');
|
|
242
|
+
|
|
243
|
+
const link = screen.getByRole('link', { name: 'Routes' });
|
|
244
|
+
expect(link).toHaveAttribute('href', '/admin/settings/routes');
|
|
245
|
+
expect(link).not.toHaveAttribute('target', '_blank');
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it('should not treat admin-like paths as admin runtime', () => {
|
|
249
|
+
const items = getTopbarPluginSettingsItems({
|
|
250
|
+
canManagePlugins: false,
|
|
251
|
+
t: (key) => key,
|
|
252
|
+
settings: [
|
|
253
|
+
{
|
|
254
|
+
key: 'system-settings',
|
|
255
|
+
name: 'system-settings',
|
|
256
|
+
title: 'System settings',
|
|
257
|
+
path: '/admin/settings/system-settings',
|
|
258
|
+
icon: null,
|
|
259
|
+
componentLoader: async () => null,
|
|
260
|
+
},
|
|
261
|
+
] as any,
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
renderSettingsLabel((items as any[])[0].label, '/admin2/foo');
|
|
265
|
+
|
|
266
|
+
const link = screen.getByRole('link', { name: 'System settings' });
|
|
267
|
+
expect(link).toHaveAttribute('href', '/nocobase/v/admin/settings/system-settings');
|
|
268
|
+
expect(link).toHaveAttribute('target', '_blank');
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it('should not duplicate the basename when building new-tab settings hrefs', () => {
|
|
272
|
+
const items = getTopbarPluginSettingsItems({
|
|
273
|
+
canManagePlugins: false,
|
|
274
|
+
t: (key) => key,
|
|
275
|
+
settings: [
|
|
276
|
+
{
|
|
277
|
+
key: 'system-settings',
|
|
278
|
+
name: 'system-settings',
|
|
279
|
+
title: 'System settings',
|
|
280
|
+
path: '/nocobase/v/admin/settings/system-settings',
|
|
281
|
+
icon: null,
|
|
282
|
+
componentLoader: async () => null,
|
|
283
|
+
},
|
|
284
|
+
] as any,
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
renderSettingsLabel((items as any[])[0].label, '/sales/p1');
|
|
288
|
+
|
|
289
|
+
const link = screen.getByRole('link', { name: 'System settings' });
|
|
290
|
+
expect(link).toHaveAttribute('href', '/nocobase/v/admin/settings/system-settings');
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('should keep external settings opening in a new tab', () => {
|
|
294
|
+
const openSpy = vi.spyOn(window, 'open').mockImplementation(() => null);
|
|
295
|
+
const items = getTopbarPluginSettingsItems({
|
|
296
|
+
canManagePlugins: false,
|
|
297
|
+
t: (key) => key,
|
|
298
|
+
settings: [
|
|
299
|
+
{
|
|
300
|
+
key: 'docs',
|
|
301
|
+
name: 'docs',
|
|
302
|
+
title: 'Docs',
|
|
303
|
+
link: 'https://www.nocobase.com/docs',
|
|
304
|
+
icon: null,
|
|
305
|
+
componentLoader: async () => null,
|
|
306
|
+
},
|
|
307
|
+
] as any,
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
renderSettingsLabel((items as any[])[0].label, '/sales/p1');
|
|
311
|
+
fireEvent.click(screen.getByText('Docs'));
|
|
312
|
+
|
|
313
|
+
expect(openSpy).toHaveBeenCalledWith('https://www.nocobase.com/docs', '_blank', 'noopener,noreferrer');
|
|
314
|
+
});
|
|
315
|
+
|
|
152
316
|
it('should return empty dropdown items when plugin manager and settings are both unavailable', () => {
|
|
153
317
|
const items = getTopbarPluginSettingsItems({
|
|
154
318
|
canManagePlugins: false,
|
|
@@ -52,6 +52,26 @@ describe('resolveAdminRouteRuntimeTarget', () => {
|
|
|
52
52
|
});
|
|
53
53
|
});
|
|
54
54
|
|
|
55
|
+
it('should resolve flowPage under a custom admin layout route path', () => {
|
|
56
|
+
expect(
|
|
57
|
+
resolveAdminRouteRuntimeTarget({
|
|
58
|
+
app,
|
|
59
|
+
layout: {
|
|
60
|
+
routePath: '/admin2',
|
|
61
|
+
},
|
|
62
|
+
route: {
|
|
63
|
+
type: NocoBaseDesktopRouteType.flowPage,
|
|
64
|
+
schemaUid: 'flow-page-1',
|
|
65
|
+
},
|
|
66
|
+
}),
|
|
67
|
+
).toEqual({
|
|
68
|
+
runtimePath: '/nocobase/v2/admin2/flow-page-1',
|
|
69
|
+
navigationMode: 'spa',
|
|
70
|
+
isLegacy: false,
|
|
71
|
+
reason: 'ok',
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
55
75
|
it('should mark page unsupported in v2 admin runtime', () => {
|
|
56
76
|
expect(
|
|
57
77
|
resolveAdminRouteRuntimeTarget({
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import { getModernClientPrefix, getV2EffectiveBasePath } from '../../../authRedirect';
|
|
11
11
|
import type { BaseApplication } from '../../../BaseApplication';
|
|
12
12
|
import { NocoBaseDesktopRouteType, type NocoBaseDesktopRoute } from '../../../flow-compat';
|
|
13
|
+
import type { LayoutDefinition } from '../../../layout-manager/types';
|
|
13
14
|
|
|
14
15
|
export type AdminRouteNavigationMode = 'spa' | 'document';
|
|
15
16
|
export type AdminRouteRuntimeTargetReason =
|
|
@@ -32,6 +33,8 @@ type LocationLike = {
|
|
|
32
33
|
hash?: string;
|
|
33
34
|
};
|
|
34
35
|
|
|
36
|
+
export type AdminLayoutRoutePathLike = Pick<LayoutDefinition, 'routePath'>;
|
|
37
|
+
|
|
35
38
|
type ResolveAdminRouteRuntimeTargetOptions = {
|
|
36
39
|
app: Pick<BaseApplication<any>, 'getPublicPath'> & {
|
|
37
40
|
router?: {
|
|
@@ -39,12 +42,14 @@ type ResolveAdminRouteRuntimeTargetOptions = {
|
|
|
39
42
|
};
|
|
40
43
|
};
|
|
41
44
|
route?: NocoBaseDesktopRoute;
|
|
45
|
+
layout?: AdminLayoutRoutePathLike;
|
|
42
46
|
location?: LocationLike;
|
|
43
47
|
preserveLocationState?: boolean;
|
|
44
48
|
log?: (message?: any, ...optionalParams: any[]) => void;
|
|
45
49
|
};
|
|
46
50
|
|
|
47
51
|
const LOG_PREFIX = '[NocoBase] Admin route runtime target:';
|
|
52
|
+
export const DEFAULT_ADMIN_LAYOUT_ROUTE_PATH = '/admin';
|
|
48
53
|
|
|
49
54
|
const EMPTY_TARGET: AdminRouteRuntimeTarget = {
|
|
50
55
|
runtimePath: null,
|
|
@@ -61,6 +66,25 @@ function normalizeRootRelativePath(pathname: string) {
|
|
|
61
66
|
return normalized;
|
|
62
67
|
}
|
|
63
68
|
|
|
69
|
+
export function getAdminLayoutRoutePath(layout?: AdminLayoutRoutePathLike | null) {
|
|
70
|
+
const routePath = typeof layout?.routePath === 'string' ? layout.routePath.trim() : '';
|
|
71
|
+
return routePath.startsWith('/') ? normalizeRootRelativePath(routePath) : DEFAULT_ADMIN_LAYOUT_ROUTE_PATH;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function joinAdminLayoutRoutePath(
|
|
75
|
+
layout: AdminLayoutRoutePathLike | null | undefined,
|
|
76
|
+
pathname?: string | number | null,
|
|
77
|
+
) {
|
|
78
|
+
const layoutRoutePath = getAdminLayoutRoutePath(layout);
|
|
79
|
+
const routePath = pathname == null ? '' : String(pathname).replace(/^\/+/, '');
|
|
80
|
+
|
|
81
|
+
if (!routePath) {
|
|
82
|
+
return layoutRoutePath;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return normalizeRootRelativePath(`${layoutRoutePath}/${routePath}`);
|
|
86
|
+
}
|
|
87
|
+
|
|
64
88
|
function normalizePublicPath(value = '/') {
|
|
65
89
|
const normalized = normalizeRootRelativePath(value || '/');
|
|
66
90
|
return normalized.endsWith('/') ? normalized : `${normalized}/`;
|
|
@@ -104,8 +128,12 @@ function joinRootRelativePath(basePath: string, pathname: string) {
|
|
|
104
128
|
return normalizeRootRelativePath(`${normalizedBasePath}${normalizedPathname.slice(1)}`);
|
|
105
129
|
}
|
|
106
130
|
|
|
107
|
-
function getV2AdminPath(
|
|
108
|
-
|
|
131
|
+
function getV2AdminPath(
|
|
132
|
+
app: ResolveAdminRouteRuntimeTargetOptions['app'],
|
|
133
|
+
schemaUid: string,
|
|
134
|
+
layout?: AdminLayoutRoutePathLike,
|
|
135
|
+
) {
|
|
136
|
+
return joinRootRelativePath(getV2EffectiveBasePath(app), joinAdminLayoutRoutePath(layout, schemaUid));
|
|
109
137
|
}
|
|
110
138
|
|
|
111
139
|
function appendLocationState(pathname: string, location?: LocationLike) {
|
|
@@ -212,7 +240,7 @@ function resolvePageRuntimeTarget(
|
|
|
212
240
|
|
|
213
241
|
if (route.type === NocoBaseDesktopRouteType.flowPage) {
|
|
214
242
|
return {
|
|
215
|
-
runtimePath: getV2AdminPath(app, route.schemaUid),
|
|
243
|
+
runtimePath: getV2AdminPath(app, route.schemaUid, options.layout),
|
|
216
244
|
navigationMode: 'spa' as const,
|
|
217
245
|
isLegacy: false,
|
|
218
246
|
reason: 'ok' as const,
|
|
@@ -229,8 +257,8 @@ function resolvePageRuntimeTarget(
|
|
|
229
257
|
return {
|
|
230
258
|
runtimePath:
|
|
231
259
|
preserveLocationState && location
|
|
232
|
-
? appendLocationState(getV2AdminPath(app, route.schemaUid), location)
|
|
233
|
-
: getV2AdminPath(app, route.schemaUid),
|
|
260
|
+
? appendLocationState(getV2AdminPath(app, route.schemaUid, options.layout), location)
|
|
261
|
+
: getV2AdminPath(app, route.schemaUid, options.layout),
|
|
234
262
|
navigationMode: 'spa' as const,
|
|
235
263
|
isLegacy: false,
|
|
236
264
|
reason: 'ok' as const,
|
|
@@ -18,7 +18,6 @@ import {
|
|
|
18
18
|
parseValueToPath,
|
|
19
19
|
isRunJSValue,
|
|
20
20
|
normalizeRunJSValue,
|
|
21
|
-
runjsWithSafeGlobals,
|
|
22
21
|
useFlowContext,
|
|
23
22
|
extractPropertyPath,
|
|
24
23
|
FlowModel,
|
|
@@ -240,7 +239,7 @@ export const DefaultValue = connect((props: Props) => {
|
|
|
240
239
|
if (isRunJSValue(out)) {
|
|
241
240
|
try {
|
|
242
241
|
const { code, version } = normalizeRunJSValue(out);
|
|
243
|
-
const ret = await
|
|
242
|
+
const ret = await model?.context?.runjs(code, undefined, { version });
|
|
244
243
|
out = ret?.success ? ret.value : undefined;
|
|
245
244
|
} catch {
|
|
246
245
|
out = undefined;
|
|
@@ -341,6 +341,8 @@ interface Props {
|
|
|
341
341
|
* 默认 false,保持历史行为。
|
|
342
342
|
*/
|
|
343
343
|
enableDateVariableAsConstant?: boolean;
|
|
344
|
+
/** 是否允许在变量选择器中使用 RunJS。默认 true,保持历史行为。 */
|
|
345
|
+
allowRunJS?: boolean;
|
|
344
346
|
maxAssociationFieldDepth?: number;
|
|
345
347
|
}
|
|
346
348
|
|
|
@@ -712,6 +714,7 @@ export const FieldAssignValueInput: React.FC<Props> = ({
|
|
|
712
714
|
preferFormItemFieldModel,
|
|
713
715
|
associationFieldNamesOverride,
|
|
714
716
|
enableDateVariableAsConstant = false,
|
|
717
|
+
allowRunJS = true,
|
|
715
718
|
maxAssociationFieldDepth = 2,
|
|
716
719
|
}) => {
|
|
717
720
|
const flowCtx = useFlowContext<FlowModelContext>();
|
|
@@ -1417,11 +1420,13 @@ export const FieldAssignValueInput: React.FC<Props> = ({
|
|
|
1417
1420
|
render: ConstantEditor,
|
|
1418
1421
|
},
|
|
1419
1422
|
{ title: tExpr('Null'), name: 'null', type: 'object', paths: ['null'], render: NullComponent },
|
|
1420
|
-
|
|
1423
|
+
...(allowRunJS
|
|
1424
|
+
? [{ title: tExpr('RunJS'), name: 'runjs', type: 'object', paths: ['runjs'], render: RunJSComponent }]
|
|
1425
|
+
: []),
|
|
1421
1426
|
...limitedBase,
|
|
1422
1427
|
];
|
|
1423
1428
|
};
|
|
1424
|
-
}, [flowCtx, ConstantEditor, NullComponent, RunJSComponent, maxAssociationFieldDepth]);
|
|
1429
|
+
}, [flowCtx, ConstantEditor, NullComponent, RunJSComponent, allowRunJS, maxAssociationFieldDepth]);
|
|
1425
1430
|
|
|
1426
1431
|
const displayValue = React.useMemo(() => {
|
|
1427
1432
|
if (!useDateVariableConstant) {
|
|
@@ -1465,7 +1470,7 @@ export const FieldAssignValueInput: React.FC<Props> = ({
|
|
|
1465
1470
|
const firstPath = meta?.paths?.[0];
|
|
1466
1471
|
if (firstPath === 'constant') return ConstantEditor;
|
|
1467
1472
|
if (firstPath === 'null') return NullComponent;
|
|
1468
|
-
if (firstPath === 'runjs') return RunJSComponent;
|
|
1473
|
+
if (allowRunJS && firstPath === 'runjs') return RunJSComponent;
|
|
1469
1474
|
return null;
|
|
1470
1475
|
},
|
|
1471
1476
|
resolveValueFromPath: (item) => {
|
|
@@ -1474,12 +1479,12 @@ export const FieldAssignValueInput: React.FC<Props> = ({
|
|
|
1474
1479
|
return useDateVariableConstant ? { type: 'today' } : '';
|
|
1475
1480
|
}
|
|
1476
1481
|
if (firstPath === 'null') return null;
|
|
1477
|
-
if (firstPath === 'runjs') return { code: '', version: 'v2' };
|
|
1482
|
+
if (allowRunJS && firstPath === 'runjs') return { code: '', version: 'v2' };
|
|
1478
1483
|
return undefined;
|
|
1479
1484
|
},
|
|
1480
1485
|
resolvePathFromValue: (currentValue) => {
|
|
1481
1486
|
if (currentValue === null) return ['null'];
|
|
1482
|
-
if (isRunJSValue(currentValue)) return ['runjs'];
|
|
1487
|
+
if (allowRunJS && isRunJSValue(currentValue)) return ['runjs'];
|
|
1483
1488
|
if (useDateVariableConstant && isCtxDateExpression(currentValue)) {
|
|
1484
1489
|
return ['constant'];
|
|
1485
1490
|
}
|
|
@@ -28,6 +28,13 @@ type FlowRouteGuardState = {
|
|
|
28
28
|
|
|
29
29
|
export type LegacyPageBehavior = 'redirect' | 'notFound' | 'bridge';
|
|
30
30
|
|
|
31
|
+
type FlowRouteLayoutContext = {
|
|
32
|
+
authCheck?: boolean;
|
|
33
|
+
routePath?: string;
|
|
34
|
+
routeName?: string;
|
|
35
|
+
uid?: string;
|
|
36
|
+
};
|
|
37
|
+
|
|
31
38
|
export type FlowRouteProps = {
|
|
32
39
|
pageUid?: string;
|
|
33
40
|
active?: boolean;
|
|
@@ -38,7 +45,7 @@ export type FlowRouteProps = {
|
|
|
38
45
|
const getDefaultAdminLayoutModel = (flowEngine: FlowEngine) =>
|
|
39
46
|
getAdminLayoutModel<AdminLayoutModel>(flowEngine, { required: true });
|
|
40
47
|
|
|
41
|
-
const getDefaultLayoutModel = (flowEngine: FlowEngine, contextLayout?:
|
|
48
|
+
const getDefaultLayoutModel = (flowEngine: FlowEngine, contextLayout?: FlowRouteLayoutContext) => {
|
|
42
49
|
const layout = contextLayout || flowEngine.context.layout;
|
|
43
50
|
|
|
44
51
|
if (layout?.uid) {
|
|
@@ -48,7 +55,10 @@ const getDefaultLayoutModel = (flowEngine: FlowEngine, contextLayout?: any) => {
|
|
|
48
55
|
return getDefaultAdminLayoutModel(flowEngine);
|
|
49
56
|
};
|
|
50
57
|
|
|
51
|
-
const getDefaultLegacyPageBehavior = (
|
|
58
|
+
const getDefaultLegacyPageBehavior = (
|
|
59
|
+
flowEngine: FlowEngine,
|
|
60
|
+
contextLayout?: FlowRouteLayoutContext,
|
|
61
|
+
): LegacyPageBehavior => {
|
|
52
62
|
const layout = contextLayout || flowEngine.context.layout;
|
|
53
63
|
|
|
54
64
|
if (layout?.routeName && layout.routeName !== 'admin') {
|
|
@@ -58,6 +68,8 @@ const getDefaultLegacyPageBehavior = (flowEngine: FlowEngine, contextLayout?: an
|
|
|
58
68
|
return 'redirect';
|
|
59
69
|
};
|
|
60
70
|
|
|
71
|
+
const shouldRequireAccessibleRoute = (contextLayout?: FlowRouteLayoutContext) => contextLayout?.authCheck !== false;
|
|
72
|
+
|
|
61
73
|
const hasFlowModel = async (flowEngine: FlowEngine, pageUid: string) => {
|
|
62
74
|
if (flowEngine.getModel(pageUid)) {
|
|
63
75
|
return true;
|
|
@@ -148,18 +160,46 @@ const BridgeFlowRoute = ({
|
|
|
148
160
|
* @throws {Error} 当缺少 `route.params.name` 时抛出异常
|
|
149
161
|
*/
|
|
150
162
|
const FlowRoute = (props: FlowRouteProps = {}) => {
|
|
163
|
+
const {
|
|
164
|
+
active,
|
|
165
|
+
getLayoutModel: getLayoutModelProp,
|
|
166
|
+
legacyPageBehavior: legacyPageBehaviorProp,
|
|
167
|
+
pageUid: pageUidProp,
|
|
168
|
+
} = props;
|
|
151
169
|
const flowEngine = useFlowEngine();
|
|
152
|
-
const flowContext = useFlowContext<
|
|
170
|
+
const flowContext = useFlowContext<{ layout?: FlowRouteLayoutContext }>();
|
|
153
171
|
const contextLayout = flowContext?.layout;
|
|
172
|
+
const propsLayoutModel = useMemo(() => getLayoutModelProp?.(flowEngine), [flowEngine, getLayoutModelProp]);
|
|
173
|
+
const rawRouteLayout = contextLayout || propsLayoutModel?.layout;
|
|
174
|
+
const routeLayoutUid = rawRouteLayout?.uid;
|
|
175
|
+
const routeLayoutRouteName = rawRouteLayout?.routeName;
|
|
176
|
+
const routeLayoutRoutePath = rawRouteLayout?.routePath;
|
|
177
|
+
const routeLayoutAuthCheck = rawRouteLayout?.authCheck;
|
|
178
|
+
const routeLayout = useMemo(() => {
|
|
179
|
+
if (
|
|
180
|
+
!routeLayoutUid &&
|
|
181
|
+
!routeLayoutRouteName &&
|
|
182
|
+
!routeLayoutRoutePath &&
|
|
183
|
+
typeof routeLayoutAuthCheck === 'undefined'
|
|
184
|
+
) {
|
|
185
|
+
return undefined;
|
|
186
|
+
}
|
|
187
|
+
return {
|
|
188
|
+
authCheck: routeLayoutAuthCheck,
|
|
189
|
+
routeName: routeLayoutRouteName,
|
|
190
|
+
routePath: routeLayoutRoutePath,
|
|
191
|
+
uid: routeLayoutUid,
|
|
192
|
+
};
|
|
193
|
+
}, [routeLayoutAuthCheck, routeLayoutRouteName, routeLayoutRoutePath, routeLayoutUid]);
|
|
154
194
|
const getLayoutModel = useMemo(
|
|
155
|
-
() =>
|
|
156
|
-
[
|
|
195
|
+
() => getLayoutModelProp || ((engine: FlowEngine) => getDefaultLayoutModel(engine, routeLayout)),
|
|
196
|
+
[getLayoutModelProp, routeLayout],
|
|
157
197
|
);
|
|
158
|
-
const legacyPageBehavior =
|
|
198
|
+
const legacyPageBehavior = legacyPageBehaviorProp || getDefaultLegacyPageBehavior(flowEngine, routeLayout);
|
|
159
199
|
const app = useApp();
|
|
160
200
|
const routeRepository = flowEngine.context.routeRepository;
|
|
161
201
|
const params = useParams();
|
|
162
|
-
const pageUid =
|
|
202
|
+
const pageUid = pageUidProp || params?.name;
|
|
163
203
|
const skipRouteRepositoryCheck = !routeRepository;
|
|
164
204
|
const [guardState, setGuardState] = useState<FlowRouteGuardState>({
|
|
165
205
|
pending: true,
|
|
@@ -196,6 +236,11 @@ const FlowRoute = (props: FlowRouteProps = {}) => {
|
|
|
196
236
|
}
|
|
197
237
|
|
|
198
238
|
const route = skipRouteRepositoryCheck ? undefined : routeRepository?.getRouteBySchemaUid?.(pageUid);
|
|
239
|
+
if (!route && !skipRouteRepositoryCheck && shouldRequireAccessibleRoute(routeLayout)) {
|
|
240
|
+
setGuardState({ pending: false, allowBridge: false, notFound: true });
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
|
|
199
244
|
if (!route && legacyPageBehavior === 'notFound') {
|
|
200
245
|
const flowModelExists = await hasFlowModel(flowEngine, pageUid);
|
|
201
246
|
if (active && requestId === requestIdRef.current) {
|
|
@@ -245,12 +290,12 @@ const FlowRoute = (props: FlowRouteProps = {}) => {
|
|
|
245
290
|
}
|
|
246
291
|
};
|
|
247
292
|
|
|
248
|
-
|
|
293
|
+
run();
|
|
249
294
|
|
|
250
295
|
return () => {
|
|
251
296
|
active = false;
|
|
252
297
|
};
|
|
253
|
-
}, [app, flowEngine, legacyPageBehavior, pageUid, routeRepository, skipRouteRepositoryCheck]);
|
|
298
|
+
}, [app, flowEngine, legacyPageBehavior, pageUid, routeLayout, routeRepository, skipRouteRepositoryCheck]);
|
|
254
299
|
|
|
255
300
|
const content = useMemo(() => {
|
|
256
301
|
if (guardState.pending) {
|
|
@@ -265,8 +310,8 @@ const FlowRoute = (props: FlowRouteProps = {}) => {
|
|
|
265
310
|
return null;
|
|
266
311
|
}
|
|
267
312
|
|
|
268
|
-
return <BridgeFlowRoute pageUid={pageUid} active={
|
|
269
|
-
}, [getLayoutModel, guardState.allowBridge, guardState.notFound, guardState.pending, pageUid
|
|
313
|
+
return <BridgeFlowRoute pageUid={pageUid} active={active} getLayoutModel={getLayoutModel} />;
|
|
314
|
+
}, [active, getLayoutModel, guardState.allowBridge, guardState.notFound, guardState.pending, pageUid]);
|
|
270
315
|
|
|
271
316
|
return content;
|
|
272
317
|
};
|
|
@@ -20,8 +20,6 @@ import {
|
|
|
20
20
|
FlowEngineProvider,
|
|
21
21
|
FlowModelRenderer,
|
|
22
22
|
ElementProxy,
|
|
23
|
-
createSafeWindow,
|
|
24
|
-
createSafeDocument,
|
|
25
23
|
createViewScopedEngine,
|
|
26
24
|
} from '@nocobase/flow-engine';
|
|
27
25
|
import { JSEditableFieldModel } from '../../../models/fields/JSEditableFieldModel';
|
|
@@ -167,11 +165,7 @@ describe('useCodeRunner (beforeRender)', () => {
|
|
|
167
165
|
const code = ctx?.inputArgs?.preview?.code || '';
|
|
168
166
|
ctx.onRefReady(ctx.ref, async (el) => {
|
|
169
167
|
ctx.defineProperty('element', { get: () => new ElementProxy(el as any) });
|
|
170
|
-
await ctx.runjs(
|
|
171
|
-
code,
|
|
172
|
-
{ window: createSafeWindow(), document: createSafeDocument() },
|
|
173
|
-
{ preprocessTemplates: true },
|
|
174
|
-
);
|
|
168
|
+
await ctx.runjs(code, undefined, { preprocessTemplates: true });
|
|
175
169
|
});
|
|
176
170
|
},
|
|
177
171
|
},
|
|
@@ -286,16 +280,7 @@ return currentUsername;
|
|
|
286
280
|
const code = ctx?.inputArgs?.preview?.code || '';
|
|
287
281
|
ctx.onRefReady(ctx.ref, async (el) => {
|
|
288
282
|
ctx.defineProperty('element', { get: () => new ElementProxy(el as any) });
|
|
289
|
-
|
|
290
|
-
await ctx.runjs(
|
|
291
|
-
code,
|
|
292
|
-
{
|
|
293
|
-
window: createSafeWindow({ navigator }),
|
|
294
|
-
document: createSafeDocument(),
|
|
295
|
-
navigator,
|
|
296
|
-
},
|
|
297
|
-
{ preprocessTemplates: true },
|
|
298
|
-
);
|
|
283
|
+
await ctx.runjs(code, undefined, { preprocessTemplates: true });
|
|
299
284
|
});
|
|
300
285
|
},
|
|
301
286
|
},
|
|
@@ -9,14 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { useCallback, useRef, useState } from 'react';
|
|
11
11
|
import { parseErrorLineColumn, WRAPPER_PRELUDE_LINES } from '../errorHelpers';
|
|
12
|
-
import {
|
|
13
|
-
FlowModelContext,
|
|
14
|
-
JSRunner,
|
|
15
|
-
createSafeWindow,
|
|
16
|
-
createSafeDocument,
|
|
17
|
-
createSafeNavigator,
|
|
18
|
-
prepareRunJsCode,
|
|
19
|
-
} from '@nocobase/flow-engine';
|
|
12
|
+
import { FlowModelContext, JSRunner, prepareRunJsCode } from '@nocobase/flow-engine';
|
|
20
13
|
|
|
21
14
|
export type RunLog = { level: 'log' | 'info' | 'warn' | 'error'; msg: string; line?: number; column?: number };
|
|
22
15
|
type RunResult = Awaited<ReturnType<JSRunner['run']>>;
|
|
@@ -286,12 +279,7 @@ export function useCodeRunner(hostCtx: FlowModelContext, version = 'v1') {
|
|
|
286
279
|
const eventName = typeof onDef === 'string' ? onDef : onDef?.eventName;
|
|
287
280
|
if (!flow) {
|
|
288
281
|
// 无可用流程(典型场景:联动规则里的 RunJS 预览),直接在当前上下文执行代码
|
|
289
|
-
|
|
290
|
-
await directRunCtx.runjs(
|
|
291
|
-
code,
|
|
292
|
-
{ window: createSafeWindow({ navigator }), document: createSafeDocument(), navigator },
|
|
293
|
-
{ version },
|
|
294
|
-
);
|
|
282
|
+
await directRunCtx.runjs(code, undefined, { version });
|
|
295
283
|
} else if (typeof eventName === 'string') {
|
|
296
284
|
await m.dispatchEvent(eventName, { preview: { code } }, { sequential: true, useCache: false });
|
|
297
285
|
} else if (isManual) {
|
|
@@ -20,10 +20,8 @@ import * as acornWalk from 'acorn-walk';
|
|
|
20
20
|
import {
|
|
21
21
|
JSRunner,
|
|
22
22
|
FlowContext,
|
|
23
|
-
createSafeDocument,
|
|
24
|
-
createSafeNavigator,
|
|
25
|
-
createSafeWindow,
|
|
26
23
|
prepareRunJsCode,
|
|
24
|
+
RUNJS_ALLOWED_BARE_GLOBAL_NAMES,
|
|
27
25
|
shouldPreprocessRunJSTemplates,
|
|
28
26
|
} from '@nocobase/flow-engine';
|
|
29
27
|
|
|
@@ -604,41 +602,7 @@ function collectHeuristicIssues(code: string): RunJSIssue[] {
|
|
|
604
602
|
return issues;
|
|
605
603
|
}
|
|
606
604
|
|
|
607
|
-
const declared = new Set<string>(
|
|
608
|
-
// Common globals / allowed runtime context roots
|
|
609
|
-
'ctx',
|
|
610
|
-
'console',
|
|
611
|
-
'window',
|
|
612
|
-
'document',
|
|
613
|
-
'navigator',
|
|
614
|
-
'Math',
|
|
615
|
-
'Date',
|
|
616
|
-
'Array',
|
|
617
|
-
'Object',
|
|
618
|
-
'Number',
|
|
619
|
-
'String',
|
|
620
|
-
'Boolean',
|
|
621
|
-
'Promise',
|
|
622
|
-
'RegExp',
|
|
623
|
-
'Set',
|
|
624
|
-
'Map',
|
|
625
|
-
'WeakSet',
|
|
626
|
-
'WeakMap',
|
|
627
|
-
'JSON',
|
|
628
|
-
'Intl',
|
|
629
|
-
'URL',
|
|
630
|
-
'Error',
|
|
631
|
-
'TypeError',
|
|
632
|
-
'encodeURIComponent',
|
|
633
|
-
'decodeURIComponent',
|
|
634
|
-
'parseInt',
|
|
635
|
-
'parseFloat',
|
|
636
|
-
'isNaN',
|
|
637
|
-
'isFinite',
|
|
638
|
-
'undefined',
|
|
639
|
-
'NaN',
|
|
640
|
-
'Infinity',
|
|
641
|
-
]);
|
|
605
|
+
const declared = new Set<string>(RUNJS_ALLOWED_BARE_GLOBAL_NAMES);
|
|
642
606
|
|
|
643
607
|
const addId = (id: any) => {
|
|
644
608
|
if (id && typeof id.name === 'string') declared.add(id.name);
|
|
@@ -1036,15 +1000,14 @@ export async function diagnoseRunJS(
|
|
|
1036
1000
|
const { consoleCapture, loggerCapture } = createLogCollectors(logs);
|
|
1037
1001
|
|
|
1038
1002
|
const baseGlobals: Record<string, any> = { console: consoleCapture };
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1003
|
+
if (typeof window !== 'undefined') {
|
|
1004
|
+
baseGlobals.window = window;
|
|
1005
|
+
if (typeof navigator !== 'undefined') {
|
|
1042
1006
|
baseGlobals.navigator = navigator;
|
|
1043
|
-
baseGlobals.window = createSafeWindow({ navigator });
|
|
1044
|
-
baseGlobals.document = createSafeDocument();
|
|
1045
1007
|
}
|
|
1046
|
-
}
|
|
1047
|
-
|
|
1008
|
+
}
|
|
1009
|
+
if (typeof document !== 'undefined') {
|
|
1010
|
+
baseGlobals.document = document;
|
|
1048
1011
|
}
|
|
1049
1012
|
|
|
1050
1013
|
let prepared = src;
|