@nocobase/client-v2 2.1.0-beta.33 → 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.
Files changed (76) hide show
  1. package/es/APIClient.d.ts +16 -0
  2. package/es/Application.d.ts +2 -1
  3. package/es/BaseApplication.d.ts +6 -0
  4. package/es/PluginManager.d.ts +2 -0
  5. package/es/authRedirect.d.ts +9 -16
  6. package/es/components/form/EnvVariableInput.d.ts +8 -6
  7. package/es/components/form/VariableInput.d.ts +73 -0
  8. package/es/components/form/index.d.ts +1 -0
  9. package/es/components/form/table/RowOverlayPreview.d.ts +27 -0
  10. package/es/components/form/table/SelectionCell.d.ts +36 -0
  11. package/es/components/form/table/Table.d.ts +82 -0
  12. package/es/components/form/table/constants.d.ts +15 -0
  13. package/es/components/form/table/dnd/SortableRow.d.ts +40 -0
  14. package/es/components/form/table/dnd/index.d.ts +9 -0
  15. package/es/components/form/table/index.d.ts +9 -0
  16. package/es/components/form/table/styles.d.ts +41 -0
  17. package/es/components/form/table/utils.d.ts +44 -0
  18. package/es/components/index.d.ts +2 -0
  19. package/es/flow/components/TextAreaWithContextSelector.d.ts +15 -0
  20. package/es/flow/models/blocks/filter-form/FilterFormBlockModel.d.ts +9 -1
  21. package/es/flow/models/blocks/table/dragSort/dragSortComponents.d.ts +1 -6
  22. package/es/flow/models/blocks/table/dragSort/dragSortHooks.d.ts +5 -1
  23. package/es/flow-compat/passwordUtils.d.ts +1 -1
  24. package/es/index.d.ts +1 -0
  25. package/es/index.mjs +166 -99
  26. package/es/json-logic/globalOperators.d.ts +11 -0
  27. package/es/theme/globalStyles.d.ts +9 -0
  28. package/es/theme/index.d.ts +1 -0
  29. package/es/utils/globalDeps.d.ts +7 -0
  30. package/lib/index.js +173 -106
  31. package/package.json +9 -6
  32. package/src/APIClient.ts +68 -0
  33. package/src/Application.tsx +6 -2
  34. package/src/BaseApplication.tsx +8 -0
  35. package/src/PluginManager.ts +2 -0
  36. package/src/__tests__/app.test.tsx +8 -0
  37. package/src/__tests__/authRedirect.test.ts +170 -64
  38. package/src/__tests__/globalDeps.test.ts +2 -0
  39. package/src/__tests__/nocobase-buildin-plugin-auth.test.tsx +6 -6
  40. package/src/__tests__/remotePlugins.test.ts +148 -0
  41. package/src/authRedirect.ts +23 -84
  42. package/src/components/form/EnvVariableInput.tsx +11 -46
  43. package/src/components/form/VariableInput.tsx +177 -0
  44. package/src/components/form/__tests__/EnvVariableInput.test.tsx +175 -0
  45. package/src/components/form/index.tsx +1 -0
  46. package/src/components/form/table/RowOverlayPreview.tsx +51 -0
  47. package/src/components/form/table/SelectionCell.tsx +72 -0
  48. package/src/components/form/table/Table.tsx +279 -0
  49. package/src/components/form/table/__tests__/Table.pagination.test.tsx +80 -0
  50. package/src/components/form/table/constants.ts +16 -0
  51. package/src/components/form/table/dnd/SortableRow.tsx +106 -0
  52. package/src/components/form/table/dnd/index.ts +10 -0
  53. package/src/components/form/table/index.tsx +13 -0
  54. package/src/components/form/table/styles.ts +110 -0
  55. package/src/components/form/table/utils.ts +75 -0
  56. package/src/components/index.ts +2 -0
  57. package/src/css-variable/CSSVariableProvider.tsx +1 -1
  58. package/src/flow/actions/filterFormDefaultValues.tsx +1 -2
  59. package/src/flow/admin-shell/admin-layout/AdminLayoutMenuModels.tsx +2 -0
  60. package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.test.ts +111 -0
  61. package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.ts +2 -1
  62. package/src/flow/components/TextAreaWithContextSelector.tsx +30 -6
  63. package/src/flow/components/code-editor/__tests__/useCodeRunner.test.tsx +81 -0
  64. package/src/flow/components/code-editor/hooks/useCodeRunner.ts +34 -2
  65. package/src/flow/models/blocks/filter-form/FilterFormBlockModel.tsx +329 -5
  66. package/src/flow/models/blocks/filter-form/__tests__/defaultValues.wiring.test.ts +337 -0
  67. package/src/flow/models/blocks/table/dragSort/dragSortComponents.tsx +1 -81
  68. package/src/flow/models/fields/JSEditableFieldModel.tsx +107 -7
  69. package/src/flow/models/fields/__tests__/JSEditableFieldModel.test.tsx +97 -0
  70. package/src/index.ts +1 -0
  71. package/src/json-logic/globalOperators.js +731 -0
  72. package/src/nocobase-buildin-plugin/index.tsx +4 -4
  73. package/src/theme/globalStyles.ts +21 -0
  74. package/src/theme/index.tsx +1 -0
  75. package/src/utils/globalDeps.ts +50 -30
  76. package/src/utils/remotePlugins.ts +107 -6
@@ -0,0 +1,731 @@
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
+ /*
11
+ Using a Universal Module Loader that should be browser, require, and AMD friendly
12
+ http://ricostacruz.com/cheatsheets/umdjs.html
13
+ */
14
+
15
+ import { getDayRangeByParams } from '@nocobase/utils/client';
16
+
17
+ export function getOperators() {
18
+ 'use strict';
19
+
20
+ if (!Array.isArray) {
21
+ Array.isArray = function (arg) {
22
+ return Object.prototype.toString.call(arg) === '[object Array]';
23
+ };
24
+ }
25
+
26
+ /**
27
+ * Return an array that contains no duplicates (original not modified)
28
+ * @param {array} array Original reference array
29
+ * @return {array} New array with no duplicates
30
+ */
31
+ function arrayUnique(array) {
32
+ var a = [];
33
+ for (var i = 0, l = array.length; i < l; i++) {
34
+ if (a.indexOf(array[i]) === -1) {
35
+ a.push(array[i]);
36
+ }
37
+ }
38
+ return a;
39
+ }
40
+ function areArraysEqual(arr1, arr2) {
41
+ return JSON.stringify(arr1) === JSON.stringify(arr2);
42
+ }
43
+
44
+ var jsonLogic = {};
45
+ var operations = {
46
+ $is: function (a, b) {
47
+ return a === b;
48
+ },
49
+ $match: function (a, b) {
50
+ if (Array.isArray(a) && Array.isArray(b) && a.some((element) => Array.isArray(element))) {
51
+ return a.some(
52
+ (subArray) => subArray?.length === b.length && subArray?.every((element, index) => element === b[index]),
53
+ );
54
+ }
55
+ return JSON.stringify(a) === JSON.stringify(b);
56
+ },
57
+ $eq: function (a, b) {
58
+ if (Array.isArray(a) && Array.isArray(b)) return areArraysEqual(a, b);
59
+ if (Array.isArray(a)) {
60
+ return a.includes(b);
61
+ }
62
+ return a == b;
63
+ },
64
+ $ne: function (a, b) {
65
+ return a != b;
66
+ },
67
+ '!==': function (a, b) {
68
+ return a !== b;
69
+ },
70
+ $gt: function (a, b) {
71
+ if (Array.isArray(a)) return a.some((k) => k > b);
72
+ return a > b;
73
+ },
74
+ $gte: function (a, b) {
75
+ return a >= b;
76
+ },
77
+ $lt: function (a, b, c) {
78
+ if (Array.isArray(a)) return a.some((k) => k < b);
79
+ return c === undefined ? a < b : a < b && b < c;
80
+ },
81
+ $lte: function (a, b, c) {
82
+ return c === undefined ? a <= b : a <= b && b <= c;
83
+ },
84
+ $exists: function (a) {
85
+ return jsonLogic.truthy(a);
86
+ },
87
+ $notEmpty: function (a) {
88
+ return !operations.$empty(a);
89
+ },
90
+ $empty: function (a) {
91
+ if (Array.isArray(a)) return a.length === 0;
92
+ if (typeof a === 'string') return a.length === 0;
93
+ return a === null || a === undefined;
94
+ },
95
+ $notExists: function (a) {
96
+ return !jsonLogic.truthy(a);
97
+ },
98
+ '%': function (a, b) {
99
+ return a % b;
100
+ },
101
+ log: function (a) {
102
+ return a;
103
+ },
104
+ $in: function (a, b) {
105
+ if (!b || typeof b.indexOf === 'undefined') return false;
106
+ if (Array.isArray(a) && Array.isArray(b)) {
107
+ return b.some((elementB) => a.includes(elementB));
108
+ }
109
+ return b.indexOf(a) !== -1;
110
+ },
111
+ $notIn: function (a, b) {
112
+ if (!b || typeof b.indexOf === 'undefined') return false;
113
+ return !(b.indexOf(a) !== -1);
114
+ },
115
+ $includes: function (a, b) {
116
+ if (!a || typeof a.indexOf === 'undefined') return false;
117
+ if (Array.isArray(a)) return a.some((element) => element?.includes(b));
118
+ return a.indexOf(b) !== -1;
119
+ },
120
+ $notIncludes: function (a, b) {
121
+ if (Array.isArray(a)) return !a.some((element) => (element || '').includes(b));
122
+
123
+ a = a || '';
124
+
125
+ return !a.includes(b);
126
+ },
127
+ $anyOf: function (a, b) {
128
+ if (a == null || a.length === 0) {
129
+ return false;
130
+ }
131
+ if (Array.isArray(a) && Array.isArray(b) && a.some((element) => Array.isArray(element))) {
132
+ return a.some((subArray) => subArray.some((element) => b.includes(element)));
133
+ }
134
+ return a.some((element) => b.includes(element));
135
+ },
136
+ $noneOf: function (a, b) {
137
+ if (!a || a.length === 0) return true;
138
+ if (!b || b.length === 0) return true;
139
+
140
+ if (!Array.isArray(a)) a = [a];
141
+ if (!Array.isArray(b)) b = [b];
142
+ return !b.some((item) => a.includes(item));
143
+ },
144
+ $notMatch: function (a, b) {
145
+ if (a?.length !== b?.length) {
146
+ return true;
147
+ }
148
+
149
+ for (let i = 0; i < (a?.length || 0); i++) {
150
+ if (a?.[i] !== b?.[i]) {
151
+ return true;
152
+ }
153
+ }
154
+ return false;
155
+ },
156
+ //日期比较操作符
157
+ $dateOn: function (a, b) {
158
+ if (!a || !b) {
159
+ return false;
160
+ }
161
+ if (b.type) {
162
+ b = getDayRangeByParams(b);
163
+ }
164
+ if (Array.isArray(b)) {
165
+ return operations.$dateBetween(a, b);
166
+ }
167
+
168
+ const dateA = parseDate(a);
169
+ const dateB = parseDate(b);
170
+ if (!dateA || !dateB) {
171
+ return false;
172
+ }
173
+
174
+ return dateA.getTime() === dateB.getTime();
175
+ },
176
+ $dateBefore: function (a, b) {
177
+ if (!a || !b) {
178
+ return false;
179
+ }
180
+ if (b.type) {
181
+ b = getDayRangeByParams(b);
182
+ }
183
+ if (Array.isArray(b)) {
184
+ b = b[0];
185
+ }
186
+ // Parse both date strings
187
+ const dateA = parseDate(a);
188
+ const dateB = parseDate(b);
189
+ if (!dateA || !dateB) {
190
+ return false;
191
+ }
192
+ return dateA.getTime() < dateB.getTime();
193
+ },
194
+ $dateNotBefore: function (a, b) {
195
+ if (!a || !b) {
196
+ return false;
197
+ }
198
+ if (b.type) {
199
+ b = getDayRangeByParams(b);
200
+ }
201
+ if (Array.isArray(b)) {
202
+ b = b[0];
203
+ }
204
+ const dateA = parseDate(a);
205
+ const dateB = parseDate(b);
206
+
207
+ if (!dateA || !dateB) {
208
+ throw new Error('Invalid date format');
209
+ }
210
+
211
+ // Compare the two dates
212
+ return dateA.getTime() >= dateB.getTime();
213
+ },
214
+ $dateAfter: function (a, b) {
215
+ if (!a || !b) {
216
+ return false;
217
+ }
218
+ if (b.type) {
219
+ b = getDayRangeByParams(b);
220
+ }
221
+ if (Array.isArray(b)) {
222
+ b = b[1];
223
+ }
224
+ // Parse both date strings
225
+ const dateA = parseDate(a);
226
+ const dateB = parseDate(b);
227
+
228
+ return dateA.getTime() > dateB.getTime();
229
+ },
230
+ $dateNotAfter: function (a, b) {
231
+ if (!a || !b) {
232
+ return false;
233
+ }
234
+ if (b.type) {
235
+ b = getDayRangeByParams(b);
236
+ }
237
+ if (Array.isArray(b)) {
238
+ b = b[1];
239
+ }
240
+ const dateA = parseDate(a);
241
+ const dateB = parseDate(b);
242
+
243
+ if (!dateA || !dateB) {
244
+ throw new Error('Invalid date format');
245
+ }
246
+ return dateA.getTime() <= dateB.getTime();
247
+ },
248
+ $dateBetween: function (a, b) {
249
+ if (!a || !b) {
250
+ return false;
251
+ }
252
+ if (b.type) {
253
+ b = getDayRangeByParams(b);
254
+ }
255
+ const dateA = parseDate(a);
256
+ const dateBStart = parseFullDate(b[0]);
257
+ const dateBEnd = parseFullDate(b[1]);
258
+ if (!dateA || !dateBStart || !dateBEnd) {
259
+ throw new Error('Invalid date format');
260
+ }
261
+ return dateA.getTime() >= dateBStart.getTime() && dateA.getTime() <= dateBEnd.getTime();
262
+ },
263
+ $dateNotOn: function (a, b) {
264
+ if (!a || !b) {
265
+ return false;
266
+ }
267
+ if (b.type) {
268
+ b = getDayRangeByParams(b);
269
+ }
270
+ if (Array.isArray(b)) {
271
+ return !operations.$dateBetween(a, b);
272
+ }
273
+ const dateA = parseDate(a);
274
+ const dateB = parseDate(b);
275
+ return dateA.getTime() !== dateB.getTime();
276
+ },
277
+ $isTruly: function (a) {
278
+ if (Array.isArray(a)) return a.some((k) => k === true || k === 1);
279
+ return a === true || a === 1;
280
+ },
281
+ $isFalsy: function (a) {
282
+ if (Array.isArray(a)) return a.some((k) => !jsonLogic.truthy(k));
283
+ return !jsonLogic.truthy(a);
284
+ },
285
+ cat: function () {
286
+ return Array.prototype.join.call(arguments, '');
287
+ },
288
+ substr: function (source, start, end) {
289
+ if (end < 0) {
290
+ // JavaScript doesn't support negative end, this emulates PHP behavior
291
+ var temp = String(source).substr(start);
292
+ return temp.substr(0, temp.length + end);
293
+ }
294
+ return String(source).substr(start, end);
295
+ },
296
+ '+': function () {
297
+ return Array.prototype.reduce.call(
298
+ arguments,
299
+ function (a, b) {
300
+ return parseFloat(a, 10) + parseFloat(b, 10);
301
+ },
302
+ 0,
303
+ );
304
+ },
305
+ '*': function () {
306
+ return Array.prototype.reduce.call(arguments, function (a, b) {
307
+ return parseFloat(a, 10) * parseFloat(b, 10);
308
+ });
309
+ },
310
+ '-': function (a, b) {
311
+ if (b === undefined) {
312
+ return -a;
313
+ } else {
314
+ return a - b;
315
+ }
316
+ },
317
+ '/': function (a, b) {
318
+ return a / b;
319
+ },
320
+ min: function () {
321
+ return Math.min.apply(this, arguments);
322
+ },
323
+ max: function () {
324
+ return Math.max.apply(this, arguments);
325
+ },
326
+ merge: function () {
327
+ return Array.prototype.reduce.call(
328
+ arguments,
329
+ function (a, b) {
330
+ return a.concat(b);
331
+ },
332
+ [],
333
+ );
334
+ },
335
+ var: function (a, b) {
336
+ var not_found = b === undefined ? null : b;
337
+ var data = this;
338
+ if (typeof a === 'undefined' || a === '' || a === null) {
339
+ return data;
340
+ }
341
+ var sub_props = String(a).split('.');
342
+ for (var i = 0; i < sub_props.length; i++) {
343
+ if (data === null || data === undefined) {
344
+ return not_found;
345
+ }
346
+ // Descending into data
347
+ data = data[sub_props[i]];
348
+ if (data === undefined) {
349
+ return not_found;
350
+ }
351
+ }
352
+ return data;
353
+ },
354
+ missing: function () {
355
+ /*
356
+ Missing can receive many keys as many arguments, like {"missing:[1,2]}
357
+ Missing can also receive *one* argument that is an array of keys,
358
+ which typically happens if it's actually acting on the output of another command
359
+ (like 'if' or 'merge')
360
+ */
361
+
362
+ var missing = [];
363
+ var keys = Array.isArray(arguments[0]) ? arguments[0] : arguments;
364
+
365
+ for (var i = 0; i < keys.length; i++) {
366
+ var key = keys[i];
367
+ var value = jsonLogic.apply({ var: key }, this);
368
+ if (value === null || value === '') {
369
+ missing.push(key);
370
+ }
371
+ }
372
+
373
+ return missing;
374
+ },
375
+ missing_some: function (need_count, options) {
376
+ // missing_some takes two arguments, how many (minimum) items must be present, and an array of keys (just like 'missing') to check for presence.
377
+ var are_missing = jsonLogic.apply({ missing: options }, this);
378
+
379
+ if (options.length - are_missing.length >= need_count) {
380
+ return [];
381
+ } else {
382
+ return are_missing;
383
+ }
384
+ },
385
+ };
386
+
387
+ jsonLogic.is_logic = function (logic) {
388
+ return (
389
+ typeof logic === 'object' && // An object
390
+ logic !== null && // but not null
391
+ !Array.isArray(logic) && // and not an array
392
+ Object.keys(logic).length === 1 &&
393
+ !logic.type // with exactly one key
394
+ );
395
+ };
396
+
397
+ /*
398
+ This helper will defer to the JsonLogic spec as a tie-breaker when different language interpreters define different behavior for the truthiness of primitives. E.g., PHP considers empty arrays to be falsy, but Javascript considers them to be truthy. JsonLogic, as an ecosystem, needs one consistent answer.
399
+
400
+ Spec and rationale here: http://jsonlogic.com/truthy
401
+ */
402
+ jsonLogic.truthy = function (value) {
403
+ if (Array.isArray(value) && value.length === 0) {
404
+ return false;
405
+ }
406
+ return !!value;
407
+ };
408
+
409
+ jsonLogic.getOperator = function (logic) {
410
+ return Object.keys(logic)[0];
411
+ };
412
+
413
+ jsonLogic.getValues = function (logic) {
414
+ return logic[jsonLogic.getOperator(logic)];
415
+ };
416
+
417
+ jsonLogic.apply = function (logic, data) {
418
+ // Does this array contain logic? Only one way to find out.
419
+ if (Array.isArray(logic)) {
420
+ return logic.map(function (l) {
421
+ return jsonLogic.apply(l, data);
422
+ });
423
+ }
424
+ // You've recursed to a primitive, stop!
425
+ if (!jsonLogic.is_logic(logic)) {
426
+ return logic;
427
+ }
428
+
429
+ var op = jsonLogic.getOperator(logic);
430
+ var values = logic[op];
431
+ var i;
432
+ var current;
433
+ var scopedLogic;
434
+ var scopedData;
435
+ var initial;
436
+
437
+ // easy syntax for unary operators, like {"var" : "x"} instead of strict {"var" : ["x"]}
438
+ if (!Array.isArray(values)) {
439
+ values = [values];
440
+ }
441
+
442
+ // 'if', 'and', and 'or' violate the normal rule of depth-first calculating consequents, let each manage recursion as needed.
443
+ if (op === 'if' || op == '?:') {
444
+ /* 'if' should be called with a odd number of parameters, 3 or greater
445
+ This works on the pattern:
446
+ if( 0 ){ 1 }else{ 2 };
447
+ if( 0 ){ 1 }else if( 2 ){ 3 }else{ 4 };
448
+ if( 0 ){ 1 }else if( 2 ){ 3 }else if( 4 ){ 5 }else{ 6 };
449
+
450
+ The implementation is:
451
+ For pairs of values (0,1 then 2,3 then 4,5 etc)
452
+ If the first evaluates truthy, evaluate and return the second
453
+ If the first evaluates falsy, jump to the next pair (e.g, 0,1 to 2,3)
454
+ given one parameter, evaluate and return it. (it's an Else and all the If/ElseIf were false)
455
+ given 0 parameters, return NULL (not great practice, but there was no Else)
456
+ */
457
+ for (i = 0; i < values.length - 1; i += 2) {
458
+ if (jsonLogic.truthy(jsonLogic.apply(values[i], data))) {
459
+ return jsonLogic.apply(values[i + 1], data);
460
+ }
461
+ }
462
+ if (values.length === i + 1) {
463
+ return jsonLogic.apply(values[i], data);
464
+ }
465
+ return null;
466
+ } else if (op === '$and') {
467
+ // Return first falsy, or last
468
+ for (i = 0; i < values.length; i += 1) {
469
+ current = jsonLogic.apply(values[i], data);
470
+ if (!jsonLogic.truthy(current)) {
471
+ return current;
472
+ }
473
+ }
474
+ return current; // Last
475
+ } else if (op === 'or') {
476
+ // Return first truthy, or last
477
+ for (i = 0; i < values.length; i += 1) {
478
+ current = jsonLogic.apply(values[i], data);
479
+ if (jsonLogic.truthy(current)) {
480
+ return current;
481
+ }
482
+ }
483
+ return current; // Last
484
+ } else if (op === 'filter') {
485
+ scopedData = jsonLogic.apply(values[0], data);
486
+ scopedLogic = values[1];
487
+
488
+ if (!Array.isArray(scopedData)) {
489
+ return [];
490
+ }
491
+ // Return only the elements from the array in the first argument,
492
+ // that return truthy when passed to the logic in the second argument.
493
+ // For parity with JavaScript, reindex the returned array
494
+ return scopedData.filter(function (datum) {
495
+ return jsonLogic.truthy(jsonLogic.apply(scopedLogic, datum));
496
+ });
497
+ } else if (op === 'map') {
498
+ scopedData = jsonLogic.apply(values[0], data);
499
+ scopedLogic = values[1];
500
+
501
+ if (!Array.isArray(scopedData)) {
502
+ return [];
503
+ }
504
+
505
+ return scopedData.map(function (datum) {
506
+ return jsonLogic.apply(scopedLogic, datum);
507
+ });
508
+ } else if (op === 'reduce') {
509
+ scopedData = jsonLogic.apply(values[0], data);
510
+ scopedLogic = values[1];
511
+ initial = typeof values[2] !== 'undefined' ? values[2] : null;
512
+
513
+ if (!Array.isArray(scopedData)) {
514
+ return initial;
515
+ }
516
+
517
+ return scopedData.reduce(function (accumulator, current) {
518
+ return jsonLogic.apply(scopedLogic, { current: current, accumulator: accumulator });
519
+ }, initial);
520
+ } else if (op === 'all') {
521
+ scopedData = jsonLogic.apply(values[0], data);
522
+ scopedLogic = values[1];
523
+ // All of an empty set is false. Note, some and none have correct fallback after the for loop
524
+ if (!Array.isArray(scopedData) || !scopedData.length) {
525
+ return false;
526
+ }
527
+ for (i = 0; i < scopedData.length; i += 1) {
528
+ if (!jsonLogic.truthy(jsonLogic.apply(scopedLogic, scopedData[i]))) {
529
+ return false; // First falsy, short circuit
530
+ }
531
+ }
532
+ return true; // All were truthy
533
+ } else if (op === 'none') {
534
+ scopedData = jsonLogic.apply(values[0], data);
535
+ scopedLogic = values[1];
536
+
537
+ if (!Array.isArray(scopedData) || !scopedData.length) {
538
+ return true;
539
+ }
540
+ for (i = 0; i < scopedData.length; i += 1) {
541
+ if (jsonLogic.truthy(jsonLogic.apply(scopedLogic, scopedData[i]))) {
542
+ return false; // First truthy, short circuit
543
+ }
544
+ }
545
+ return true; // None were truthy
546
+ } else if (op === 'some') {
547
+ scopedData = jsonLogic.apply(values[0], data);
548
+ scopedLogic = values[1];
549
+
550
+ if (!Array.isArray(scopedData) || !scopedData.length) {
551
+ return false;
552
+ }
553
+ for (i = 0; i < scopedData.length; i += 1) {
554
+ if (jsonLogic.truthy(jsonLogic.apply(scopedLogic, scopedData[i]))) {
555
+ return true; // First truthy, short circuit
556
+ }
557
+ }
558
+ return false; // None were truthy
559
+ }
560
+ // Everyone else gets immediate depth-first recursion
561
+ values = values.map(function (val) {
562
+ return jsonLogic.apply(val, data);
563
+ });
564
+
565
+ // The operation is called with "data" bound to its "this" and "values" passed as arguments.
566
+ // Structured commands like % or > can name formal arguments while flexible commands (like missing or merge) can operate on the pseudo-array arguments
567
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments
568
+ if (Object.prototype.hasOwnProperty.call(operations, op) && typeof operations[op] === 'function') {
569
+ return operations[op].apply(data, values);
570
+ } else if (op.indexOf('.') > 0) {
571
+ // Contains a dot, and not in the 0th position
572
+ var sub_ops = String(op).split('.');
573
+ var operation = operations;
574
+ for (i = 0; i < sub_ops.length; i++) {
575
+ if (!Object.prototype.hasOwnProperty.call(operation, sub_ops[i])) {
576
+ throw new Error('Unrecognized operation ' + op + ' (failed at ' + sub_ops.slice(0, i + 1).join('.') + ')');
577
+ }
578
+ // Descending into operations
579
+ operation = operation[sub_ops[i]];
580
+ }
581
+ return operation.apply(data, values);
582
+ }
583
+ throw new Error('Unrecognized operation ' + op);
584
+ };
585
+
586
+ jsonLogic.uses_data = function (logic) {
587
+ var collection = [];
588
+
589
+ if (jsonLogic.is_logic(logic)) {
590
+ var op = jsonLogic.getOperator(logic);
591
+ var values = logic[op];
592
+
593
+ if (!Array.isArray(values)) {
594
+ values = [values];
595
+ }
596
+
597
+ if (op === 'var') {
598
+ // This doesn't cover the case where the arg to var is itself a rule.
599
+ collection.push(values[0]);
600
+ } else {
601
+ // Recursion!
602
+ values.forEach(function (val) {
603
+ collection.push.apply(collection, jsonLogic.uses_data(val));
604
+ });
605
+ }
606
+ }
607
+
608
+ return arrayUnique(collection);
609
+ };
610
+
611
+ jsonLogic.addOperation = function (name, code) {
612
+ operations[name] = code;
613
+ };
614
+
615
+ jsonLogic.rmOperation = function (name) {
616
+ delete operations[name];
617
+ };
618
+
619
+ jsonLogic.rule_like = function (rule, pattern) {
620
+ // console.log("Is ". JSON.stringify(rule) . " like " . JSON.stringify(pattern) . "?");
621
+ if (pattern === rule) {
622
+ return true;
623
+ } // TODO : Deep object equivalency?
624
+ if (pattern === '@') {
625
+ return true;
626
+ } // Wildcard!
627
+ if (pattern === 'number') {
628
+ return typeof rule === 'number';
629
+ }
630
+ if (pattern === 'string') {
631
+ return typeof rule === 'string';
632
+ }
633
+ if (pattern === 'array') {
634
+ // !logic test might be superfluous in JavaScript
635
+ return Array.isArray(rule) && !jsonLogic.is_logic(rule);
636
+ }
637
+
638
+ if (jsonLogic.is_logic(pattern)) {
639
+ if (jsonLogic.is_logic(rule)) {
640
+ var pattern_op = jsonLogic.getOperator(pattern);
641
+ var rule_op = jsonLogic.getOperator(rule);
642
+
643
+ if (pattern_op === '@' || pattern_op === rule_op) {
644
+ // echo "\nOperators match, go deeper\n";
645
+ return jsonLogic.rule_like(jsonLogic.get_values(rule, false), jsonLogic.get_values(pattern, false));
646
+ }
647
+ }
648
+ return false; // pattern is logic, rule isn't, can't be eq
649
+ }
650
+
651
+ if (Array.isArray(pattern)) {
652
+ if (Array.isArray(rule)) {
653
+ if (pattern.length !== rule.length) {
654
+ return false;
655
+ }
656
+ /*
657
+ Note, array order MATTERS, because we're using this array test logic to consider arguments, where order can matter. (e.g., + is commutative, but '-' or 'if' or 'var' are NOT)
658
+ */
659
+ for (var i = 0; i < pattern.length; i += 1) {
660
+ // If any fail, we fail
661
+ if (!jsonLogic.rule_like(rule[i], pattern[i])) {
662
+ return false;
663
+ }
664
+ }
665
+ return true; // If they *all* passed, we pass
666
+ } else {
667
+ return false; // Pattern is array, rule isn't
668
+ }
669
+ }
670
+
671
+ // Not logic, not array, not a === match for rule.
672
+ return false;
673
+ };
674
+
675
+ return jsonLogic;
676
+ }
677
+
678
+ function parseFullDate(dateStr) {
679
+ if (dateStr.includes('T') && dateStr.endsWith('Z')) {
680
+ // ISO 格式,包含时区(如 '2025-06-05T16:00:00.000Z')
681
+ return new Date(dateStr);
682
+ }
683
+
684
+ if (dateStr.includes(' ')) {
685
+ // 有日期+时间(如 '2025-06-06 23:59:59')
686
+ return new Date(dateStr.replace(' ', 'T'));
687
+ }
688
+
689
+ // 只有日期(如 '2025-06-06')
690
+ return new Date(`${dateStr}T00:00:00`);
691
+ }
692
+
693
+ function parseMonth(dateStr) {
694
+ const [year, month] = dateStr.split('-').map(Number);
695
+ return new Date(year, month - 1);
696
+ }
697
+
698
+ function parseQuarter(dateStr) {
699
+ const year = parseInt(dateStr.slice(0, 4));
700
+ const quarter = parseInt(dateStr.slice(5, 6));
701
+ const month = (quarter - 1) * 3;
702
+ return new Date(year, month);
703
+ }
704
+
705
+ function parseYear(dateStr) {
706
+ const year = parseInt(dateStr);
707
+ return new Date(year, 0);
708
+ }
709
+
710
+ function parseDate(targetDateStr) {
711
+ let dateStr = Array.isArray(targetDateStr) ? targetDateStr[1] ?? targetDateStr[0] : targetDateStr;
712
+ if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(dateStr)) {
713
+ return new Date(dateStr);
714
+ } else if (/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(dateStr)) {
715
+ return new Date(dateStr.replace(' ', 'T'));
716
+ } else if (/^\d{4}-\d{2}-\d{2}$/.test(dateStr)) {
717
+ // YYYY-MM-DD 格式
718
+ return parseFullDate(dateStr);
719
+ } else if (/^\d{4}-\d{2}$/.test(dateStr)) {
720
+ // YYYY-MM 格式
721
+ return parseMonth(dateStr);
722
+ } else if (/^\d{4}Q[1-4]$/.test(dateStr)) {
723
+ // YYYYQn 格式
724
+ return parseQuarter(dateStr);
725
+ } else if (/^\d{4}$/.test(dateStr)) {
726
+ // YYYY 格式
727
+ return parseYear(dateStr);
728
+ }
729
+
730
+ return null;
731
+ }