@nocobase/client-v2 2.1.0-beta.34 → 2.1.0-beta.35

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocobase/client-v2",
3
- "version": "2.1.0-beta.34",
3
+ "version": "2.1.0-beta.35",
4
4
  "license": "Apache-2.0",
5
5
  "main": "lib/index.js",
6
6
  "module": "es/index.mjs",
@@ -26,10 +26,11 @@
26
26
  "@formily/antd-v5": "1.2.3",
27
27
  "@formily/react": "^2.2.27",
28
28
  "@formily/shared": "^2.2.27",
29
- "@nocobase/evaluators": "2.1.0-beta.34",
30
- "@nocobase/flow-engine": "2.1.0-beta.34",
31
- "@nocobase/sdk": "2.1.0-beta.34",
32
- "@nocobase/shared": "2.1.0-beta.34",
29
+ "@nocobase/evaluators": "2.1.0-beta.35",
30
+ "@nocobase/flow-engine": "2.1.0-beta.35",
31
+ "@nocobase/sdk": "2.1.0-beta.35",
32
+ "@nocobase/shared": "2.1.0-beta.35",
33
+ "@nocobase/utils": "2.1.0-beta.35",
33
34
  "ahooks": "^3.7.2",
34
35
  "antd": "5.24.2",
35
36
  "antd-style": "3.7.1",
@@ -43,5 +44,5 @@
43
44
  "react-i18next": "^11.15.1",
44
45
  "react-router-dom": "^6.30.1"
45
46
  },
46
- "gitHead": "ca804833299c547f8d49f8d58f73273a4bfcd03c"
47
+ "gitHead": "74310d8b9e9581fcde14b5a93d12b41ddb5bb325"
47
48
  }
@@ -40,6 +40,7 @@ import type {
40
40
  RouterOptions,
41
41
  } from './RouterManager';
42
42
  import { WebSocketClient, type WebSocketClientOptions } from './WebSocketClient';
43
+ import { getOperators } from './json-logic/globalOperators';
43
44
  import { compose, normalizeContainer } from './utils';
44
45
  import { defineGlobalDeps } from './utils/globalDeps';
45
46
  import { getRequireJs } from './utils/requirejs';
@@ -57,6 +58,11 @@ type AuthTokenPayload = {
57
58
  token: string;
58
59
  authenticator: string | null;
59
60
  };
61
+ export type JsonLogic = {
62
+ apply: (logic: any, data?: any) => any;
63
+ addOperation: (name: string, fn?: any) => void;
64
+ rmOperation: (name: string) => void;
65
+ };
60
66
 
61
67
  const LEADING_SLASHES_REGEXP = /^\/+/;
62
68
  const TRAILING_SLASHES_REGEXP = /\/+$/;
@@ -123,6 +129,7 @@ export abstract class BaseApplication<
123
129
  public favicon!: string;
124
130
  public flowEngine: FlowEngine;
125
131
  public dataSourceManager: any;
132
+ public jsonLogic!: JsonLogic;
126
133
  public context: FlowEngineContext & {
127
134
  routeRepository: RouteRepository;
128
135
  appInfo: Promise<Record<string, any>>;
@@ -220,6 +227,7 @@ export abstract class BaseApplication<
220
227
 
221
228
  protected afterManagersInitialized() {
222
229
  this.aiManager = new AIManager(this);
230
+ this.jsonLogic = getOperators();
223
231
  }
224
232
 
225
233
  protected configureContext() {
@@ -26,6 +26,8 @@ export type PluginData = {
26
26
  version: string;
27
27
  url: string;
28
28
  clientV2Url?: string;
29
+ devMode?: 'esm';
30
+ appDevDependencies?: string[];
29
31
  type: 'local' | 'upload' | 'npm';
30
32
  };
31
33
 
@@ -48,6 +48,14 @@ describe('app', () => {
48
48
  expect(app.getHref('/test')).toBe('/test');
49
49
  });
50
50
 
51
+ it('should initialize shared jsonLogic operators', () => {
52
+ const app = new Application({ router });
53
+
54
+ expect(app.jsonLogic.apply({ $eq: [1, '1'] })).toBe(true);
55
+ app.jsonLogic.addOperation('$testAlwaysTrue', () => true);
56
+ expect(app.jsonLogic.apply({ $testAlwaysTrue: [] })).toBe(true);
57
+ });
58
+
51
59
  it('should apply the provided favicon immediately', () => {
52
60
  const app = new Application({ router });
53
61
 
@@ -14,8 +14,11 @@ import { configRequirejs, defineDevPlugins, getPlugins } from '../utils/remotePl
14
14
  describe('client-v2 remotePlugins', () => {
15
15
  afterEach(() => {
16
16
  window.define = undefined;
17
+ window.__nocobase_app_dev_plugins__ = undefined;
17
18
  });
18
19
 
20
+ const createModuleUrl = (code: string) => `data:text/javascript;charset=utf-8,${encodeURIComponent(code)}`;
21
+
19
22
  it('should define dev plugins with /client-v2 module ids', () => {
20
23
  class DemoPlugin extends Plugin {}
21
24
 
@@ -58,6 +61,151 @@ describe('client-v2 remotePlugins', () => {
58
61
  expect(mockDefine).not.toHaveBeenCalledWith('@nocobase/demo/client', expect.any(Function));
59
62
  });
60
63
 
64
+ it('should request remote plugins when devDynamicImport only resolves some plugins', async () => {
65
+ class DemoPlugin extends Plugin {}
66
+
67
+ const remoteFn = vi.fn();
68
+ const requirejs: any = {
69
+ requirejs: (pluginData, resolve) => {
70
+ remoteFn();
71
+ resolve({ default: DemoPlugin });
72
+ },
73
+ };
74
+ requirejs.requirejs.config = vi.fn();
75
+
76
+ const mockDefine: any = vi.fn();
77
+ window.define = mockDefine;
78
+
79
+ const plugins = await getPlugins({
80
+ requirejs,
81
+ pluginData: [
82
+ {
83
+ name: '@nocobase/demo',
84
+ packageName: '@nocobase/demo',
85
+ url: 'https://demo.com/dist/client-v2/index.js',
86
+ },
87
+ {
88
+ name: '@nocobase/remote',
89
+ packageName: '@nocobase/remote',
90
+ url: 'https://remote.com/dist/client-v2/index.js',
91
+ },
92
+ ] as any,
93
+ devDynamicImport: ((packageName) => {
94
+ if (packageName === '@nocobase/demo') {
95
+ return Promise.resolve({ default: DemoPlugin });
96
+ }
97
+ return Promise.resolve(null);
98
+ }) as any,
99
+ });
100
+
101
+ expect(plugins).toEqual([
102
+ ['@nocobase/demo', DemoPlugin],
103
+ ['@nocobase/remote', DemoPlugin],
104
+ ]);
105
+ expect(remoteFn).toHaveBeenCalledTimes(1);
106
+ expect(mockDefine).toHaveBeenCalledTimes(1);
107
+ expect(requirejs.requirejs.config).toHaveBeenCalledWith({
108
+ waitSeconds: 120,
109
+ paths: {
110
+ '@nocobase/remote/client-v2': 'https://remote.com/dist/client-v2/index.js',
111
+ },
112
+ });
113
+ });
114
+
115
+ it('should register ESM dev plugins before importing dependent plugins', async () => {
116
+ const requirejs: any = {
117
+ requirejs: vi.fn(),
118
+ };
119
+ requirejs.requirejs.config = vi.fn();
120
+
121
+ const mockDefine: any = vi.fn();
122
+ window.define = mockDefine;
123
+
124
+ const plugins = await getPlugins({
125
+ requirejs,
126
+ pluginData: [
127
+ {
128
+ name: '@nocobase/dependent',
129
+ packageName: '@nocobase/dependent',
130
+ url: createModuleUrl(`
131
+ const dependency = window.__nocobase_app_dev_plugins__ && window.__nocobase_app_dev_plugins__['@nocobase/dependency/client-v2'];
132
+ export default class DependentPlugin extends dependency.default {
133
+ static SharedBase = dependency.SharedBase;
134
+ }
135
+ `),
136
+ devMode: 'esm',
137
+ appDevDependencies: ['@nocobase/dependency'],
138
+ },
139
+ {
140
+ name: '@nocobase/dependency',
141
+ packageName: '@nocobase/dependency',
142
+ url: createModuleUrl('export class SharedBase {}; export default class DependencyPlugin {}'),
143
+ devMode: 'esm',
144
+ },
145
+ ] as any,
146
+ });
147
+
148
+ const dependencyModule = window.__nocobase_app_dev_plugins__['@nocobase/dependency/client-v2'] as {
149
+ default?: unknown;
150
+ SharedBase?: unknown;
151
+ };
152
+ const dependentPlugin = plugins[1][1] as (typeof plugins)[number][1] & { SharedBase?: unknown };
153
+
154
+ expect(plugins.map(([name]) => name)).toEqual(['@nocobase/dependency', '@nocobase/dependent']);
155
+ expect(dependencyModule.SharedBase).toBeDefined();
156
+ expect(plugins[0][1]).toBe(dependencyModule.default);
157
+ expect(dependentPlugin.SharedBase).toBe(dependencyModule.SharedBase);
158
+ expect(mockDefine).toHaveBeenCalledWith('@nocobase/dependency/client-v2', expect.any(Function));
159
+ expect(requirejs.requirejs.config).not.toHaveBeenCalled();
160
+ });
161
+
162
+ it('should load RequireJS dependencies before ESM dev plugins', async () => {
163
+ class DependencyPlugin extends Plugin {}
164
+ const SharedBase = { source: 'remote' };
165
+ const dependencyModule = {
166
+ SharedBase,
167
+ default: DependencyPlugin,
168
+ };
169
+ const requirejs: any = {
170
+ requirejs: vi.fn((packageNames, resolve) => {
171
+ expect(packageNames).toEqual(['@nocobase/remote-dependency/client-v2']);
172
+ resolve(dependencyModule);
173
+ }),
174
+ };
175
+ requirejs.requirejs.config = vi.fn();
176
+
177
+ window.define = vi.fn();
178
+
179
+ const plugins = await getPlugins({
180
+ requirejs,
181
+ pluginData: [
182
+ {
183
+ name: '@nocobase/dependent',
184
+ packageName: '@nocobase/dependent',
185
+ url: createModuleUrl(`
186
+ const dependency = window.__nocobase_app_dev_plugins__ && window.__nocobase_app_dev_plugins__['@nocobase/remote-dependency/client-v2'];
187
+ export default class DependentPlugin extends dependency.default {
188
+ static SharedBase = dependency.SharedBase;
189
+ }
190
+ `),
191
+ devMode: 'esm',
192
+ appDevDependencies: ['@nocobase/remote-dependency'],
193
+ },
194
+ {
195
+ name: '@nocobase/remote-dependency',
196
+ packageName: '@nocobase/remote-dependency',
197
+ url: 'https://dependency.com',
198
+ },
199
+ ] as any,
200
+ });
201
+
202
+ const dependentPlugin = plugins[1][1] as (typeof plugins)[number][1] & { SharedBase?: unknown };
203
+
204
+ expect(plugins.map(([name]) => name)).toEqual(['@nocobase/remote-dependency', '@nocobase/dependent']);
205
+ expect(window.__nocobase_app_dev_plugins__['@nocobase/remote-dependency/client-v2']).toBe(dependencyModule);
206
+ expect(dependentPlugin.SharedBase).toBe(SharedBase);
207
+ });
208
+
61
209
  it('should configure remote plugin paths with /client-v2 module ids', () => {
62
210
  const requirejs: any = {
63
211
  requirejs: {
@@ -9,8 +9,8 @@
9
9
 
10
10
  import { TinyColor } from '@ctrl/tinycolor';
11
11
  import { useEffect } from 'react';
12
- import { CustomToken, defaultTheme } from '@nocobase/client-v2';
13
12
  import { theme } from 'antd';
13
+ import { type CustomToken, defaultTheme } from '../theme';
14
14
 
15
15
  interface Result extends ReturnType<typeof theme.useToken> {
16
16
  token: CustomToken;
@@ -122,12 +122,11 @@ const FilterFormDefaultValuesUI = observer(
122
122
  rootCollection={getCollectionFromModel(ctx.model)}
123
123
  value={value}
124
124
  onChange={handleChange}
125
- fixedMode="default"
126
- showCondition={false}
127
125
  showValueEditorWhenNoField
128
126
  getValueInputProps={getValueInputProps}
129
127
  isTitleFieldCandidate={isTitleFieldCandidate}
130
128
  onSyncAssociationTitleField={onSyncAssociationTitleField}
129
+ enableDateVariableAsConstant
131
130
  />
132
131
  );
133
132
  },