@react-querybuilder/core 8.22.5 → 8.23.0

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 (120) hide show
  1. package/derivations/package.json +4 -0
  2. package/dist/cjs/react-querybuilder_core.cjs.development.d.ts +2078 -1665
  3. package/dist/cjs/react-querybuilder_core.cjs.development.js +1293 -422
  4. package/dist/cjs/react-querybuilder_core.cjs.development.js.map +1 -1
  5. package/dist/cjs/react-querybuilder_core.cjs.production.d.ts +2078 -1665
  6. package/dist/cjs/react-querybuilder_core.cjs.production.js +1 -1
  7. package/dist/cjs/react-querybuilder_core.cjs.production.js.map +1 -1
  8. package/dist/{convertQuery-Cw76XFIn.js → convertQuery-BLT0GIhM.js} +17 -8
  9. package/dist/{convertQuery-Cw76XFIn.js.map → convertQuery-BLT0GIhM.js.map} +1 -1
  10. package/dist/{convertQuery-D_4J7E-Z.mjs → convertQuery-DaXv3h7S.mjs} +6 -3
  11. package/dist/{convertQuery-D_4J7E-Z.mjs.map → convertQuery-DaXv3h7S.mjs.map} +1 -1
  12. package/dist/derivations.d.mts +2902 -0
  13. package/dist/derivations.d.ts +2902 -0
  14. package/dist/derivations.js +2845 -0
  15. package/dist/derivations.js.map +1 -0
  16. package/dist/derivations.mjs +2678 -0
  17. package/dist/derivations.mjs.map +1 -0
  18. package/dist/formatQuery.d.mts +2 -895
  19. package/dist/formatQuery.d.ts +2 -895
  20. package/dist/formatQuery.js +120 -140
  21. package/dist/formatQuery.js.map +1 -1
  22. package/dist/formatQuery.mjs +8 -28
  23. package/dist/formatQuery.mjs.map +1 -1
  24. package/dist/{import-DVWI7Fdh.d.mts → import-BdKPtLzo.d.mts} +2 -2
  25. package/dist/{import-6ExjhifL.d.ts → import-BfZTwhp2.d.ts} +2 -2
  26. package/dist/{index-CX9mehdL.d.mts → index-DOIE2pwx.d.mts} +274 -3
  27. package/dist/{index-CX9mehdL.d.ts → index-DOIE2pwx.d.ts} +274 -3
  28. package/dist/index-OF2Q1gTp.d.mts +895 -0
  29. package/dist/index-XueTy_XQ.d.ts +895 -0
  30. package/dist/isRuleOrGroupValid-CIMZNkt4.mjs +27 -0
  31. package/dist/isRuleOrGroupValid-CIMZNkt4.mjs.map +1 -0
  32. package/dist/isRuleOrGroupValid-CtUf4_4x.js +38 -0
  33. package/dist/isRuleOrGroupValid-CtUf4_4x.js.map +1 -0
  34. package/dist/{isRuleGroup-CqLThsbu.mjs → objectUtils-DDZ-KhV5.mjs} +25 -2
  35. package/dist/objectUtils-DDZ-KhV5.mjs.map +1 -0
  36. package/dist/{isRuleGroup-0JNiQgLg.js → objectUtils-DfnEmDSV.js} +48 -1
  37. package/dist/objectUtils-DfnEmDSV.js.map +1 -0
  38. package/dist/parseCEL.d.mts +2 -2
  39. package/dist/parseCEL.d.ts +2 -2
  40. package/dist/parseCEL.js +19 -16
  41. package/dist/parseCEL.js.map +1 -1
  42. package/dist/parseCEL.mjs +12 -9
  43. package/dist/parseCEL.mjs.map +1 -1
  44. package/dist/parseCypher.d.mts +1 -1
  45. package/dist/parseCypher.d.ts +1 -1
  46. package/dist/parseGremlin.d.mts +1 -1
  47. package/dist/parseGremlin.d.ts +1 -1
  48. package/dist/parseJSONata.d.mts +2 -2
  49. package/dist/parseJSONata.d.ts +2 -2
  50. package/dist/parseJSONata.js +17 -14
  51. package/dist/parseJSONata.js.map +1 -1
  52. package/dist/parseJSONata.mjs +11 -8
  53. package/dist/parseJSONata.mjs.map +1 -1
  54. package/dist/parseJsonLogic.d.mts +2 -2
  55. package/dist/parseJsonLogic.d.ts +2 -2
  56. package/dist/parseJsonLogic.js +38 -38
  57. package/dist/parseJsonLogic.js.map +1 -1
  58. package/dist/parseJsonLogic.mjs +5 -5
  59. package/dist/parseMongoDB.d.mts +2 -2
  60. package/dist/parseMongoDB.d.ts +2 -2
  61. package/dist/parseMongoDB.js +50 -47
  62. package/dist/parseMongoDB.js.map +1 -1
  63. package/dist/parseMongoDB.mjs +35 -32
  64. package/dist/parseMongoDB.mjs.map +1 -1
  65. package/dist/parseSPARQL.d.mts +1 -1
  66. package/dist/parseSPARQL.d.ts +1 -1
  67. package/dist/parseSQL.d.mts +2 -2
  68. package/dist/parseSQL.d.ts +2 -2
  69. package/dist/parseSQL.js +27 -23
  70. package/dist/parseSQL.js.map +1 -1
  71. package/dist/parseSQL.mjs +21 -17
  72. package/dist/parseSQL.mjs.map +1 -1
  73. package/dist/parseSpEL.d.mts +2 -2
  74. package/dist/parseSpEL.d.ts +2 -2
  75. package/dist/parseSpEL.js +16 -13
  76. package/dist/parseSpEL.js.map +1 -1
  77. package/dist/parseSpEL.mjs +12 -9
  78. package/dist/parseSpEL.mjs.map +1 -1
  79. package/dist/parserUtils-BKPstAaO.mjs +30 -0
  80. package/dist/parserUtils-BKPstAaO.mjs.map +1 -0
  81. package/dist/parserUtils-DbBXyAht.js +41 -0
  82. package/dist/parserUtils-DbBXyAht.js.map +1 -0
  83. package/dist/{prepareQueryObjects-BnGL91Tt.js → prepareQueryObjects-Dj9cTl9D.js} +90 -63
  84. package/dist/prepareQueryObjects-Dj9cTl9D.js.map +1 -0
  85. package/dist/{prepareQueryObjects-ChINDIap.mjs → prepareQueryObjects-Dmyf3Wi_.mjs} +56 -59
  86. package/dist/prepareQueryObjects-Dmyf3Wi_.mjs.map +1 -0
  87. package/dist/react-querybuilder_core.d.mts +2078 -1665
  88. package/dist/react-querybuilder_core.legacy-esm.d.ts +2078 -1665
  89. package/dist/react-querybuilder_core.legacy-esm.js +1445 -635
  90. package/dist/react-querybuilder_core.legacy-esm.js.map +1 -1
  91. package/dist/react-querybuilder_core.mjs +1288 -424
  92. package/dist/react-querybuilder_core.mjs.map +1 -1
  93. package/dist/react-querybuilder_core.production.d.mts +2078 -1665
  94. package/dist/react-querybuilder_core.production.mjs +1 -1
  95. package/dist/react-querybuilder_core.production.mjs.map +1 -1
  96. package/dist/transformQuery-B7d9AWUf.d.mts +116 -0
  97. package/dist/transformQuery-BXDiUBGB.d.ts +116 -0
  98. package/dist/transformQuery.d.mts +2 -116
  99. package/dist/transformQuery.d.ts +2 -116
  100. package/dist/transformQuery.js +3 -4
  101. package/dist/transformQuery.js.map +1 -1
  102. package/dist/transformQuery.mjs +1 -2
  103. package/dist/transformQuery.mjs.map +1 -1
  104. package/dist/utils-CP5Jqy1P.mjs +1294 -0
  105. package/dist/utils-CP5Jqy1P.mjs.map +1 -0
  106. package/dist/utils-e-7KHkjV.js +1779 -0
  107. package/dist/utils-e-7KHkjV.js.map +1 -0
  108. package/package.json +33 -22
  109. package/dist/isRuleGroup-0JNiQgLg.js.map +0 -1
  110. package/dist/isRuleGroup-CqLThsbu.mjs.map +0 -1
  111. package/dist/objectUtils-Cr02aGxb.js +0 -27
  112. package/dist/objectUtils-Cr02aGxb.js.map +0 -1
  113. package/dist/objectUtils-DBlmJgBf.mjs +0 -16
  114. package/dist/objectUtils-DBlmJgBf.mjs.map +0 -1
  115. package/dist/prepareQueryObjects-BnGL91Tt.js.map +0 -1
  116. package/dist/prepareQueryObjects-ChINDIap.mjs.map +0 -1
  117. package/dist/utils-4k-j5FBa.mjs +0 -537
  118. package/dist/utils-4k-j5FBa.mjs.map +0 -1
  119. package/dist/utils-C79dxwZb.js +0 -752
  120. package/dist/utils-C79dxwZb.js.map +0 -1
@@ -1,6 +1,647 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  let numeric_quantity = require("numeric-quantity");
3
3
  let immer = require("immer");
4
+ //#region src/controlPropKeys.ts
5
+ /**
6
+ * Runtime data describing the set of query builder controls and the props each one receives.
7
+ *
8
+ * These live in `@react-querybuilder/core` (not `react-querybuilder`) so that non-React ports
9
+ * can consume them without a React dependency.
10
+ *
11
+ * Nothing here can drift from the React implementation unnoticed:
12
+ * `react-querybuilder`'s `types/controlPropKeys.test.ts` gates {@link controlKeys} against
13
+ * `defaultControlElements` and `ControlElementsProp`, and every entry of
14
+ * {@link controlPropKeys} against the corresponding props interface—both at runtime and at
15
+ * compile time, in both directions.
16
+ */
17
+ /**
18
+ * The name of every query builder control, alphabetized.
19
+ *
20
+ * @group Props
21
+ */
22
+ const controlKeys = [
23
+ "actionElement",
24
+ "addGroupAction",
25
+ "addRuleAction",
26
+ "cloneGroupAction",
27
+ "cloneRuleAction",
28
+ "combinatorSelector",
29
+ "dragHandle",
30
+ "fieldSelector",
31
+ "inlineCombinator",
32
+ "lockGroupAction",
33
+ "lockRuleAction",
34
+ "matchModeEditor",
35
+ "muteGroupAction",
36
+ "muteRuleAction",
37
+ "notToggle",
38
+ "operatorSelector",
39
+ "removeGroupAction",
40
+ "removeRuleAction",
41
+ "rule",
42
+ "ruleGroup",
43
+ "ruleGroupBodyElements",
44
+ "ruleGroupHeaderElements",
45
+ "shiftActions",
46
+ "undoRedoActions",
47
+ "valueEditor",
48
+ "valueSelector",
49
+ "valueSourceSelector"
50
+ ];
51
+ /**
52
+ * The full set of prop names each default control receives. Ports can use this to declare
53
+ * every prop explicitly instead of relying on fall-through/attribute inheritance.
54
+ *
55
+ * Each array is the complete, alphabetized key set of the control's props interface in
56
+ * `react-querybuilder`. The lists are written out in full rather than composed from shared
57
+ * bases because `--isolatedDeclarations` cannot infer array types built with spreads. A
58
+ * compile-time gate in `react-querybuilder` (`types/controlPropKeys.test.ts`) fails the build
59
+ * if any list drifts from its interface in either direction.
60
+ *
61
+ * @group Props
62
+ */
63
+ const controlPropKeys = {
64
+ actionElement: [
65
+ "className",
66
+ "context",
67
+ "disabled",
68
+ "disabledTranslation",
69
+ "handleOnClick",
70
+ "label",
71
+ "level",
72
+ "path",
73
+ "ruleOrGroup",
74
+ "rules",
75
+ "schema",
76
+ "testID",
77
+ "title",
78
+ "validation"
79
+ ],
80
+ addGroupAction: [
81
+ "className",
82
+ "context",
83
+ "disabled",
84
+ "disabledTranslation",
85
+ "handleOnClick",
86
+ "label",
87
+ "level",
88
+ "path",
89
+ "ruleOrGroup",
90
+ "rules",
91
+ "schema",
92
+ "testID",
93
+ "title",
94
+ "validation"
95
+ ],
96
+ addRuleAction: [
97
+ "className",
98
+ "context",
99
+ "disabled",
100
+ "disabledTranslation",
101
+ "handleOnClick",
102
+ "label",
103
+ "level",
104
+ "path",
105
+ "ruleOrGroup",
106
+ "rules",
107
+ "schema",
108
+ "testID",
109
+ "title",
110
+ "validation"
111
+ ],
112
+ cloneGroupAction: [
113
+ "className",
114
+ "context",
115
+ "disabled",
116
+ "disabledTranslation",
117
+ "handleOnClick",
118
+ "label",
119
+ "level",
120
+ "path",
121
+ "ruleOrGroup",
122
+ "rules",
123
+ "schema",
124
+ "testID",
125
+ "title",
126
+ "validation"
127
+ ],
128
+ cloneRuleAction: [
129
+ "className",
130
+ "context",
131
+ "disabled",
132
+ "disabledTranslation",
133
+ "handleOnClick",
134
+ "label",
135
+ "level",
136
+ "path",
137
+ "ruleOrGroup",
138
+ "rules",
139
+ "schema",
140
+ "testID",
141
+ "title",
142
+ "validation"
143
+ ],
144
+ combinatorSelector: [
145
+ "className",
146
+ "context",
147
+ "disabled",
148
+ "handleOnChange",
149
+ "level",
150
+ "options",
151
+ "path",
152
+ "ruleGroup",
153
+ "rules",
154
+ "schema",
155
+ "testID",
156
+ "title",
157
+ "validation",
158
+ "value"
159
+ ],
160
+ dragHandle: [
161
+ "className",
162
+ "context",
163
+ "disabled",
164
+ "dragHandleAttributes",
165
+ "label",
166
+ "level",
167
+ "path",
168
+ "ruleOrGroup",
169
+ "schema",
170
+ "testID",
171
+ "title",
172
+ "validation"
173
+ ],
174
+ fieldSelector: [
175
+ "className",
176
+ "context",
177
+ "disabled",
178
+ "handleOnChange",
179
+ "level",
180
+ "operator",
181
+ "options",
182
+ "path",
183
+ "rule",
184
+ "schema",
185
+ "testID",
186
+ "title",
187
+ "validation",
188
+ "value"
189
+ ],
190
+ inlineCombinator: [
191
+ "className",
192
+ "component",
193
+ "context",
194
+ "disabled",
195
+ "handleOnChange",
196
+ "level",
197
+ "options",
198
+ "path",
199
+ "ruleGroup",
200
+ "rules",
201
+ "schema",
202
+ "testID",
203
+ "title",
204
+ "validation",
205
+ "value"
206
+ ],
207
+ lockGroupAction: [
208
+ "className",
209
+ "context",
210
+ "disabled",
211
+ "disabledTranslation",
212
+ "handleOnClick",
213
+ "label",
214
+ "level",
215
+ "path",
216
+ "ruleOrGroup",
217
+ "rules",
218
+ "schema",
219
+ "testID",
220
+ "title",
221
+ "validation"
222
+ ],
223
+ lockRuleAction: [
224
+ "className",
225
+ "context",
226
+ "disabled",
227
+ "disabledTranslation",
228
+ "handleOnClick",
229
+ "label",
230
+ "level",
231
+ "path",
232
+ "ruleOrGroup",
233
+ "rules",
234
+ "schema",
235
+ "testID",
236
+ "title",
237
+ "validation"
238
+ ],
239
+ matchModeEditor: [
240
+ "className",
241
+ "classNames",
242
+ "context",
243
+ "disabled",
244
+ "field",
245
+ "fieldData",
246
+ "handleOnChange",
247
+ "level",
248
+ "match",
249
+ "numericEditorComponent",
250
+ "options",
251
+ "path",
252
+ "rule",
253
+ "schema",
254
+ "selectorComponent",
255
+ "testID",
256
+ "thresholdPlaceholder",
257
+ "title",
258
+ "validation",
259
+ "value"
260
+ ],
261
+ muteGroupAction: [
262
+ "className",
263
+ "context",
264
+ "disabled",
265
+ "disabledTranslation",
266
+ "handleOnClick",
267
+ "label",
268
+ "level",
269
+ "path",
270
+ "ruleOrGroup",
271
+ "rules",
272
+ "schema",
273
+ "testID",
274
+ "title",
275
+ "validation"
276
+ ],
277
+ muteRuleAction: [
278
+ "className",
279
+ "context",
280
+ "disabled",
281
+ "disabledTranslation",
282
+ "handleOnClick",
283
+ "label",
284
+ "level",
285
+ "path",
286
+ "ruleOrGroup",
287
+ "rules",
288
+ "schema",
289
+ "testID",
290
+ "title",
291
+ "validation"
292
+ ],
293
+ notToggle: [
294
+ "checked",
295
+ "className",
296
+ "context",
297
+ "disabled",
298
+ "handleOnChange",
299
+ "label",
300
+ "level",
301
+ "path",
302
+ "ruleGroup",
303
+ "schema",
304
+ "testID",
305
+ "title",
306
+ "validation"
307
+ ],
308
+ operatorSelector: [
309
+ "className",
310
+ "context",
311
+ "disabled",
312
+ "field",
313
+ "fieldData",
314
+ "handleOnChange",
315
+ "level",
316
+ "options",
317
+ "path",
318
+ "rule",
319
+ "schema",
320
+ "testID",
321
+ "title",
322
+ "validation",
323
+ "value"
324
+ ],
325
+ removeGroupAction: [
326
+ "className",
327
+ "context",
328
+ "disabled",
329
+ "disabledTranslation",
330
+ "handleOnClick",
331
+ "label",
332
+ "level",
333
+ "path",
334
+ "ruleOrGroup",
335
+ "rules",
336
+ "schema",
337
+ "testID",
338
+ "title",
339
+ "validation"
340
+ ],
341
+ removeRuleAction: [
342
+ "className",
343
+ "context",
344
+ "disabled",
345
+ "disabledTranslation",
346
+ "handleOnClick",
347
+ "label",
348
+ "level",
349
+ "path",
350
+ "ruleOrGroup",
351
+ "rules",
352
+ "schema",
353
+ "testID",
354
+ "title",
355
+ "validation"
356
+ ],
357
+ rule: [
358
+ "actions",
359
+ "context",
360
+ "disabled",
361
+ "dndRef",
362
+ "dragHandleAttributes",
363
+ "dragMonitorId",
364
+ "dragRef",
365
+ "dropEffect",
366
+ "dropMonitorId",
367
+ "dropNotAllowed",
368
+ "field",
369
+ "groupItems",
370
+ "id",
371
+ "isDragging",
372
+ "isOver",
373
+ "operator",
374
+ "parentDisabled",
375
+ "parentMuted",
376
+ "path",
377
+ "rule",
378
+ "schema",
379
+ "shiftDownDisabled",
380
+ "shiftUpDisabled",
381
+ "translations",
382
+ "value",
383
+ "valueSource"
384
+ ],
385
+ ruleGroup: [
386
+ "actions",
387
+ "combinator",
388
+ "context",
389
+ "disabled",
390
+ "dragHandleAttributes",
391
+ "dragMonitorId",
392
+ "dragRef",
393
+ "dropEffect",
394
+ "dropMonitorId",
395
+ "dropNotAllowed",
396
+ "dropRef",
397
+ "groupItems",
398
+ "id",
399
+ "isDragging",
400
+ "isOver",
401
+ "not",
402
+ "parentDisabled",
403
+ "parentMuted",
404
+ "path",
405
+ "previewRef",
406
+ "ruleGroup",
407
+ "rules",
408
+ "schema",
409
+ "shiftDownDisabled",
410
+ "shiftUpDisabled",
411
+ "translations"
412
+ ],
413
+ ruleGroupBodyElements: [
414
+ "accessibleDescription",
415
+ "actions",
416
+ "addGroup",
417
+ "addRule",
418
+ "classNames",
419
+ "cloneGroup",
420
+ "combinator",
421
+ "context",
422
+ "disabled",
423
+ "dragHandleAttributes",
424
+ "dragMonitorId",
425
+ "dragRef",
426
+ "dropEffect",
427
+ "dropMonitorId",
428
+ "dropNotAllowed",
429
+ "dropRef",
430
+ "groupItems",
431
+ "id",
432
+ "isDragging",
433
+ "isOver",
434
+ "muted",
435
+ "not",
436
+ "onCombinatorChange",
437
+ "onGroupAdd",
438
+ "onIndependentCombinatorChange",
439
+ "onNotToggleChange",
440
+ "outerClassName",
441
+ "parentDisabled",
442
+ "parentMuted",
443
+ "path",
444
+ "pathsMemo",
445
+ "previewRef",
446
+ "removeGroup",
447
+ "ruleGroup",
448
+ "rules",
449
+ "schema",
450
+ "shiftDownDisabled",
451
+ "shiftGroupDown",
452
+ "shiftGroupUp",
453
+ "shiftUpDisabled",
454
+ "toggleLockGroup",
455
+ "toggleMuteGroup",
456
+ "translations",
457
+ "validationClassName",
458
+ "validationResult"
459
+ ],
460
+ ruleGroupHeaderElements: [
461
+ "accessibleDescription",
462
+ "actions",
463
+ "addGroup",
464
+ "addRule",
465
+ "classNames",
466
+ "cloneGroup",
467
+ "combinator",
468
+ "context",
469
+ "disabled",
470
+ "dragHandleAttributes",
471
+ "dragMonitorId",
472
+ "dragRef",
473
+ "dropEffect",
474
+ "dropMonitorId",
475
+ "dropNotAllowed",
476
+ "dropRef",
477
+ "groupItems",
478
+ "id",
479
+ "isDragging",
480
+ "isOver",
481
+ "muted",
482
+ "not",
483
+ "onCombinatorChange",
484
+ "onGroupAdd",
485
+ "onIndependentCombinatorChange",
486
+ "onNotToggleChange",
487
+ "outerClassName",
488
+ "parentDisabled",
489
+ "parentMuted",
490
+ "path",
491
+ "pathsMemo",
492
+ "previewRef",
493
+ "removeGroup",
494
+ "ruleGroup",
495
+ "rules",
496
+ "schema",
497
+ "shiftDownDisabled",
498
+ "shiftGroupDown",
499
+ "shiftGroupUp",
500
+ "shiftUpDisabled",
501
+ "toggleLockGroup",
502
+ "toggleMuteGroup",
503
+ "translations",
504
+ "validationClassName",
505
+ "validationResult"
506
+ ],
507
+ shiftActions: [
508
+ "className",
509
+ "context",
510
+ "disabled",
511
+ "labels",
512
+ "level",
513
+ "path",
514
+ "ruleOrGroup",
515
+ "schema",
516
+ "shiftDown",
517
+ "shiftDownDisabled",
518
+ "shiftUp",
519
+ "shiftUpDisabled",
520
+ "testID",
521
+ "title",
522
+ "titles",
523
+ "validation"
524
+ ],
525
+ undoRedoActions: [
526
+ "className",
527
+ "classNames",
528
+ "context",
529
+ "disabled",
530
+ "labels",
531
+ "level",
532
+ "path",
533
+ "ruleOrGroup",
534
+ "schema",
535
+ "testID",
536
+ "title",
537
+ "titles",
538
+ "validation"
539
+ ],
540
+ valueEditor: [
541
+ "className",
542
+ "context",
543
+ "disabled",
544
+ "field",
545
+ "fieldData",
546
+ "handleOnChange",
547
+ "inputType",
548
+ "level",
549
+ "listsAsArrays",
550
+ "operator",
551
+ "parseNumbers",
552
+ "path",
553
+ "rule",
554
+ "schema",
555
+ "selectorComponent",
556
+ "separator",
557
+ "skipHook",
558
+ "testID",
559
+ "title",
560
+ "type",
561
+ "validation",
562
+ "value",
563
+ "valueSource",
564
+ "values"
565
+ ],
566
+ valueSelector: [
567
+ "className",
568
+ "context",
569
+ "disabled",
570
+ "handleOnChange",
571
+ "level",
572
+ "listsAsArrays",
573
+ "multiple",
574
+ "options",
575
+ "path",
576
+ "schema",
577
+ "testID",
578
+ "title",
579
+ "validation",
580
+ "value"
581
+ ],
582
+ valueSourceSelector: [
583
+ "className",
584
+ "context",
585
+ "disabled",
586
+ "field",
587
+ "fieldData",
588
+ "handleOnChange",
589
+ "level",
590
+ "options",
591
+ "path",
592
+ "rule",
593
+ "schema",
594
+ "testID",
595
+ "title",
596
+ "validation",
597
+ "value"
598
+ ]
599
+ };
600
+ /**
601
+ * Which bulk override, if any, applies to each control: `"action"` for controls that a supplied
602
+ * `actionElement` replaces, `"selector"` for controls that a supplied `valueSelector` replaces,
603
+ * and `null` for controls that neither replaces.
604
+ *
605
+ * `actionElement` and `valueSelector` are themselves `null`—they are the _source_ of a bulk
606
+ * override, not a target of one.
607
+ *
608
+ * Replaces name-suffix sniffing (`key.endsWith('Action')`), which would silently misclassify a
609
+ * future control named e.g. `pathSelector`, and which disagrees between implementations about
610
+ * whether `shiftActions`/`undoRedoActions` (plural) match. They do not: neither receives the
611
+ * `actionElement` bulk override.
612
+ *
613
+ * @group Props
614
+ */
615
+ const controlKind = {
616
+ actionElement: null,
617
+ addGroupAction: "action",
618
+ addRuleAction: "action",
619
+ cloneGroupAction: "action",
620
+ cloneRuleAction: "action",
621
+ combinatorSelector: "selector",
622
+ dragHandle: null,
623
+ fieldSelector: "selector",
624
+ inlineCombinator: null,
625
+ lockGroupAction: "action",
626
+ lockRuleAction: "action",
627
+ matchModeEditor: null,
628
+ muteGroupAction: "action",
629
+ muteRuleAction: "action",
630
+ notToggle: null,
631
+ operatorSelector: "selector",
632
+ removeGroupAction: "action",
633
+ removeRuleAction: "action",
634
+ rule: null,
635
+ ruleGroup: null,
636
+ ruleGroupBodyElements: null,
637
+ ruleGroupHeaderElements: null,
638
+ shiftActions: null,
639
+ undoRedoActions: null,
640
+ valueEditor: null,
641
+ valueSelector: null,
642
+ valueSourceSelector: "selector"
643
+ };
644
+ //#endregion
4
645
  //#region src/defaults.ts
5
646
  /**
6
647
  * @group Defaults
@@ -649,6 +1290,31 @@ const defaultMaxHistory = 50;
649
1290
  */
650
1291
  const defaultCoalesceMs = 500;
651
1292
  //#endregion
1293
+ //#region src/utils/abortReasons.ts
1294
+ /**
1295
+ * Abort reasons that {@link QueryManager}'s `strict` mode treats as errors. The remaining
1296
+ * reasons—`"same-location"` and `"no-change"`—describe valid operations that had nothing to do,
1297
+ * so they are reported to `onInvalidTarget` but never throw.
1298
+ *
1299
+ * Declared apart from `QueryManager` so the `@react-querybuilder/core/derivations` entry point can
1300
+ * export it without putting the manager in its module graph.
1301
+ *
1302
+ * @group Query Tools
1303
+ */
1304
+ const strictAbortReasons = [
1305
+ "target-not-found",
1306
+ "parent-not-found",
1307
+ "parent-not-a-group",
1308
+ "destination-not-found",
1309
+ "root-not-allowed",
1310
+ "not-a-combinator-slot",
1311
+ "target-disabled",
1312
+ "parent-disabled",
1313
+ "max-levels-exceeded"
1314
+ ];
1315
+ /** @internal */
1316
+ const strictAbortReasonSet = new Set(strictAbortReasons);
1317
+ //#endregion
652
1318
  //#region src/utils/arrayUtils.ts
653
1319
  /**
654
1320
  * Splits a string by a given character (see {@link defaultJoinChar}). Escaped characters
@@ -751,6 +1417,94 @@ const isRuleGroupType = (rg) => isRuleGroup(rg) && typeof rg.combinator === "str
751
1417
  */
752
1418
  const isRuleGroupTypeIC = (rg) => isRuleGroup(rg) && rg.combinator === void 0;
753
1419
  //#endregion
1420
+ //#region src/utils/signature.ts
1421
+ /**
1422
+ * Signature returned when a change alters the _shape_ of the query—rules or groups added,
1423
+ * removed, moved, or reordered—rather than the properties of a single node. Structural changes
1424
+ * never coalesce with each other, so each one produces its own history entry.
1425
+ */
1426
+ const structuralSignature = "~structural";
1427
+ /**
1428
+ * Signature returned when two queries differ by reference only, with no observable difference
1429
+ * in their properties.
1430
+ */
1431
+ const unchangedSignature = "";
1432
+ /**
1433
+ * Returns the list of property names that differ between two nodes, ignoring `rules` (which is
1434
+ * compared structurally by the caller).
1435
+ */
1436
+ const changedProps = (prev, next) => {
1437
+ const keys = /* @__PURE__ */ new Set([...Object.keys(prev), ...Object.keys(next)]);
1438
+ keys.delete("rules");
1439
+ const changed = [];
1440
+ for (const key of keys) if (!Object.is(prev[key], next[key])) changed.push(key);
1441
+ return changed.toSorted();
1442
+ };
1443
+ const signatureOfNode = (prev, next, parentId, index) => {
1444
+ if (prev === next) return "";
1445
+ if (typeof prev === "string" || typeof next === "string") return typeof prev === typeof next ? `${parentId}:combinator[${index}]` : structuralSignature;
1446
+ const prevIsGroup = isRuleGroup(prev);
1447
+ const nextIsGroup = isRuleGroup(next);
1448
+ if (prevIsGroup !== nextIsGroup) return structuralSignature;
1449
+ if (!prevIsGroup || !nextIsGroup) {
1450
+ const props = changedProps(prev, next);
1451
+ return props.length === 0 ? "" : `${next.id ?? ""}:${props.join(",")}`;
1452
+ }
1453
+ if (prev.rules.length !== next.rules.length) return structuralSignature;
1454
+ const ownProps = changedProps(prev, next);
1455
+ let changedIndex = -1;
1456
+ for (const [i, prevRule] of prev.rules.entries()) if (prevRule !== next.rules[i]) {
1457
+ if (changedIndex !== -1) return structuralSignature;
1458
+ changedIndex = i;
1459
+ }
1460
+ if (changedIndex === -1) return ownProps.length === 0 ? "" : `${next.id ?? ""}:${ownProps.join(",")}`;
1461
+ if (ownProps.length > 0) return structuralSignature;
1462
+ return signatureOfNode(prev.rules[changedIndex], next.rules[changedIndex], next.id ?? "", changedIndex);
1463
+ };
1464
+ /**
1465
+ * Describes _what changed_ between two versions of a query as a short string, so that
1466
+ * consecutive edits to the same property of the same rule (e.g. typing in a value editor) can
1467
+ * be recognized and coalesced into a single history entry.
1468
+ *
1469
+ * Returns {@link structuralSignature} for changes to the shape of the query, {@link unchangedSignature} when the
1470
+ * two queries differ by reference only, and `"<id>:<props>"` otherwise.
1471
+ *
1472
+ * The walk prunes on reference equality, so it costs O(depth) rather than O(size) for the
1473
+ * single-node edits that make up the overwhelming majority of changes.
1474
+ *
1475
+ * Note that this relies on the structural sharing that Immer—and therefore every query
1476
+ * produced by RQB's own update functions—guarantees: nodes that did not change keep their
1477
+ * identity. A query that has been wholly rebuilt (deep-cloned, round-tripped through JSON,
1478
+ * re-parsed from a string) shares no identity with its predecessor, so it is reported as
1479
+ * {@link structuralSignature} and will not coalesce. That is a graceful degradation—every change simply
1480
+ * gets its own history entry—but controlled components that clone the query on every change
1481
+ * will not benefit from coalescing.
1482
+ */
1483
+ const signatureOf = (prev, next) => signatureOfNode(prev, next, "", -1);
1484
+ //#endregion
1485
+ //#region src/utils/coalesce.ts
1486
+ /**
1487
+ * Determines whether a change should be absorbed into the current history entry instead of
1488
+ * pushing a new one. This is the exact rule {@link QueryManager} applies when recording history,
1489
+ * exposed so non-React implementations can manage their own history stacks without
1490
+ * reimplementing (and drifting from) the semantics.
1491
+ *
1492
+ * A change coalesces only when all three hold:
1493
+ * - the change is not structural (rules/groups added, removed, moved, or reordered)
1494
+ * - its signature matches the previously recorded signature
1495
+ * - it occurred within `coalesceMs` of the previous recording
1496
+ *
1497
+ * Note that {@link unchangedSignature} is not handled here. A change with no observable
1498
+ * difference is never recorded at all, which is a separate decision made before this check.
1499
+ *
1500
+ * @param prevSig Signature of the previously recorded change, or `undefined` if there is none.
1501
+ * @param nextSig Signature of the change being recorded, from {@link signatureOf}.
1502
+ * @param prevAt Timestamp of the previous recording.
1503
+ * @param now Timestamp of the change being recorded.
1504
+ * @param coalesceMs Coalescing window in milliseconds.
1505
+ */
1506
+ const shouldCoalesce = (prevSig, nextSig, prevAt, now, coalesceMs = 500) => nextSig !== "~structural" && nextSig === prevSig && now >= prevAt && now - prevAt < coalesceMs;
1507
+ //#endregion
754
1508
  //#region src/utils/convertQuery.ts
755
1509
  const combinatorLevels = [
756
1510
  "or",
@@ -1264,6 +2018,20 @@ const uniqOptList = (originalArray) => {
1264
2018
  if (isFlexibleOptionGroupArray(originalArray)) return uniqOptGroups(originalArray);
1265
2019
  return uniqByIdentifier(originalArray.map((o) => toFullOption(o)));
1266
2020
  };
2021
+ /**
2022
+ * Normalizes any accepted option list shape—array, option groups, or a record—into a
2023
+ * `FullOptionList`, applying `baseOption` properties and (when `autoSelectOption` is `false`)
2024
+ * a leading placeholder option. Also returns the flattened lookup map and the option that
2025
+ * should be selected by default.
2026
+ *
2027
+ * Note: `labelMap` overrides only apply to array/option group input (where string options are
2028
+ * expanded); record input derives labels from the record values themselves.
2029
+ *
2030
+ * This is the same normalization `<QueryBuilder />` applies to `fields`, `operators`,
2031
+ * `combinators`, and value lists, so a non-React implementation resolves options identically.
2032
+ *
2033
+ * @group Option Lists
2034
+ */
1267
2035
  const prepareOptionList = (props) => {
1268
2036
  // v8 ignore next
1269
2037
  const { optionList: optionListPropOriginal, baseOption = {}, labelMap = {}, placeholder: { placeholderName = "~", placeholderLabel = defaultPlaceholderLabel, placeholderGroupLabel = defaultPlaceholderLabel } = {}, autoSelectOption = true } = props;
@@ -4562,8 +5330,10 @@ const defaultRuleProcessorParameterized = (rule, opts, meta) => {
4562
5330
  return finalize(`${qPre}${rule.field}${qPost} ${sqlOperator} ${paramPrefix}${firstParamName} and ${paramPrefix}${secondParamName}`);
4563
5331
  }
4564
5332
  let paramValue = rule.value;
4565
- if (typeof rule.value === "string") if (shouldRenderAsNumber(rule.value, parseNumbers)) paramValue = parseNumber(rule.value, { parseNumbers });
4566
- else paramValue = /^'.*'$/g.test(value) ? value.replaceAll(/(^'|'$)/g, "") : /* v8 ignore next -- @preserve */ value;
5333
+ if (typeof rule.value === "string") {
5334
+ if (shouldRenderAsNumber(rule.value, parseNumbers)) paramValue = parseNumber(rule.value, { parseNumbers });
5335
+ else paramValue = /^'.*'$/g.test(value) ? value.replaceAll(/(^'|'$)/g, "") : /* v8 ignore next -- @preserve */ value;
5336
+ }
4567
5337
  let paramName = "";
4568
5338
  if (parameterized) params.push(paramValue);
4569
5339
  else {
@@ -5285,6 +6055,12 @@ const derivePathInfo = (path, childCount, { disabled = false, disabledPaths = []
5285
6055
  };
5286
6056
  //#endregion
5287
6057
  //#region src/utils/generateAccessibleDescription.ts
6058
+ /**
6059
+ * The default `accessibleDescriptionGenerator`. Produces the `aria-label` for a rule group:
6060
+ * `"Query builder"` for the root group, `"Rule group at path 0-1"` for any other.
6061
+ *
6062
+ * @group Accessibility
6063
+ */
5288
6064
  const generateAccessibleDescription = (params) => pathsAreEqual([], params.path) ? `Query builder` : `Rule group at path ${params.path.join("-")}`;
5289
6065
  //#endregion
5290
6066
  //#region src/utils/generateID.ts
@@ -5323,6 +6099,29 @@ if (cryptoModule) {
5323
6099
  }
5324
6100
  }
5325
6101
  //#endregion
6102
+ //#region src/utils/immerInstances.ts
6103
+ /**
6104
+ * Globally enables or disables immer's auto-freeze, i.e. the deep freeze applied to every query
6105
+ * returned by {@link add}, {@link update}, {@link remove}, {@link move}, {@link insert}, and
6106
+ * {@link group}.
6107
+ *
6108
+ * Re-exported from immer so that consumers mixing these tools with their own `produce` calls can
6109
+ * switch both at once. Prefer the per-call `freeze` option, or the {@link QueryManager} option of
6110
+ * the same name, when the change should not be process-wide. A per-call `freeze: false` wins
6111
+ * regardless of this setting; a per-call `freeze: true` does not re-enable freezing once this has
6112
+ * turned it off, since it selects immer's default instance.
6113
+ *
6114
+ * @group Query Tools
6115
+ */
6116
+ const setAutoFreeze = immer.setAutoFreeze;
6117
+ const unfrozenImmer = new immer.Immer({ autoFreeze: false });
6118
+ /**
6119
+ * The `produce` implementation matching the requested freeze behavior.
6120
+ *
6121
+ * @internal
6122
+ */
6123
+ const producerFor = (freeze = true) => freeze ? immer.produce : unfrozenImmer.produce;
6124
+ //#endregion
5326
6125
  //#region src/utils/getMatchModesUtil.ts
5327
6126
  const dummyFD$1 = {
5328
6127
  name: "name",
@@ -5575,6 +6374,46 @@ const resolveValueList = ({ field, operator, fieldData, getValues, placeholder,
5575
6374
  autoSelectOption
5576
6375
  }).optionList;
5577
6376
  //#endregion
6377
+ //#region src/utils/optionsEqual.ts
6378
+ const isPlainObject = (x) => {
6379
+ const proto = Object.getPrototypeOf(x);
6380
+ return proto === null || proto === Object.prototype;
6381
+ };
6382
+ /**
6383
+ * Structural equality for {@link QueryManagerOptions} values. Arrays and plain objects are
6384
+ * compared by value; everything else — functions, class instances, `Map`s, `Date`s — by
6385
+ * identity.
6386
+ *
6387
+ * That split is what makes an options object rebuilt on every render compare equal as long as
6388
+ * its _data_ did not change, which is the entire point: a caller that passes object literals
6389
+ * (`fields={[...]}`, `translations={{...}}`) hands a fresh identity to every reconfigure, so an
6390
+ * identity-only comparison would report a change every time.
6391
+ *
6392
+ * Comparing functions by identity is deliberate rather than a limitation. Two functions cannot
6393
+ * be proven equivalent, so a caller that rebuilds `getDefaultValue` per render must either
6394
+ * memoize it or accept the reconfigure.
6395
+ */
6396
+ const valuesEqual = (a, b) => {
6397
+ if (Object.is(a, b)) return true;
6398
+ if (Array.isArray(a) || Array.isArray(b)) return Array.isArray(a) && Array.isArray(b) && a.length === b.length && a.every((v, i) => valuesEqual(v, b[i]));
6399
+ if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return false;
6400
+ if (Object.getPrototypeOf(a) !== Object.getPrototypeOf(b) || !isPlainObject(a)) return false;
6401
+ const aKeys = Object.keys(a);
6402
+ return aKeys.length === Object.keys(b).length && aKeys.every((k) => valuesEqual(a[k], b[k]));
6403
+ };
6404
+ /**
6405
+ * Whether two {@link QueryManagerOptions} objects describe the same configuration, per
6406
+ * {@link valuesEqual}: data by value, functions by identity.
6407
+ *
6408
+ * {@link QueryManager.reconfigure} uses this to gate itself, so most callers never need it
6409
+ * directly. It is exported for framework adapters that decide whether to _call_ `reconfigure`
6410
+ * at all — skipping the call avoids building the merged options object in the first place.
6411
+ *
6412
+ * Nested option objects (`history`, `translations`, …) are descended into rather than compared
6413
+ * by identity, so `{ history: { maxHistory: 10 } }` rebuilt per render compares equal.
6414
+ */
6415
+ const optionsEqual = (a, b) => valuesEqual(a, b);
6416
+ //#endregion
5578
6417
  //#region src/utils/preferProp.ts
5579
6418
  const preferPropDefaultTrue = (prop, context) => prop === false ? false : prop ? true : !(context === false);
5580
6419
  const preferPropDefaultFalse = (prop, context) => prop ? true : prop === false ? false : !!context;
@@ -5711,7 +6550,7 @@ const exceedsMaxLevels = (parentPath, { maxLevels = Infinity } = {}) => !!parent
5711
6550
  *
5712
6551
  * @group Query Tools
5713
6552
  */
5714
- const add = (query, ruleOrGroup, parentPathOrID, options = {}) => (0, immer.produce)(query, (q) => addInPlace(q, ruleOrGroup, parentPathOrID, options));
6553
+ const add = (query, ruleOrGroup, parentPathOrID, options = {}) => producerFor(options.freeze)(query, (q) => addInPlace(q, ruleOrGroup, parentPathOrID, options));
5715
6554
  /**
5716
6555
  * Adds a rule or group to a query in place.
5717
6556
  *
@@ -5803,6 +6642,11 @@ const normalizeUpdateArgs = (a, b, c, d) => {
5803
6642
  };
5804
6643
  };
5805
6644
  /**
6645
+ * The options argument of an {@link update} call, whose position depends on the argument form.
6646
+ * Cheaper than {@link normalizeUpdateArgs} when only the options are needed.
6647
+ */
6648
+ const updateOptionsOf = (a, b, c, d) => (typeof a === "string" || Array.isArray(a) ? d : c) ?? {};
6649
+ /**
5806
6650
  * Applies one or more property updates to a query in place. Shared by
5807
6651
  * {@link update} and {@link updateInPlace}.
5808
6652
  */
@@ -5823,7 +6667,7 @@ const applyUpdatesInPlace = (query, a, b, c, d) => {
5823
6667
  *
5824
6668
  * @group Query Tools
5825
6669
  */
5826
- const update = ((query, a, b, c, d) => (0, immer.produce)(query, (q) => {
6670
+ const update = ((query, a, b, c, d) => producerFor(updateOptionsOf(a, b, c, d).freeze)(query, (q) => {
5827
6671
  applyUpdatesInPlace(q, a, b, c, d);
5828
6672
  }));
5829
6673
  /**
@@ -5871,13 +6715,14 @@ const updateInPlaceSingle = (query, prop, value, pathOrID, options = {}) => {
5871
6715
  return query;
5872
6716
  }
5873
6717
  const parentRules = parent.rules;
5874
- if (path.at(-1) % 2 === 1) if (parentRules[path.at(-1)] === value) onAbort?.({
5875
- reason: "no-change",
5876
- operation: "update",
5877
- pathOrID
5878
- });
5879
- else parentRules[path.at(-1)] = value;
5880
- else onAbort?.({
6718
+ if (path.at(-1) % 2 === 1) {
6719
+ if (parentRules[path.at(-1)] === value) onAbort?.({
6720
+ reason: "no-change",
6721
+ operation: "update",
6722
+ pathOrID
6723
+ });
6724
+ else parentRules[path.at(-1)] = value;
6725
+ } else onAbort?.({
5881
6726
  reason: "not-a-combinator-slot",
5882
6727
  operation: "update",
5883
6728
  pathOrID
@@ -5947,7 +6792,7 @@ const updateInPlaceSingle = (query, prop, value, pathOrID, options = {}) => {
5947
6792
  *
5948
6793
  * @group Query Tools
5949
6794
  */
5950
- const remove = (query, pathOrID, options = {}) => (0, immer.produce)(query, (q) => removeInPlace(q, pathOrID, options));
6795
+ const remove = (query, pathOrID, options = {}) => producerFor(options.freeze)(query, (q) => removeInPlace(q, pathOrID, options));
5951
6796
  /**
5952
6797
  * Removes a rule or group from a query in place.
5953
6798
  *
@@ -6002,25 +6847,28 @@ const removeInPlace = (query, pathOrID, options = {}) => {
6002
6847
  const getNextPath = (query, currentPath, newPathOrShiftDirection) => {
6003
6848
  if (Array.isArray(newPathOrShiftDirection)) return newPathOrShiftDirection;
6004
6849
  const ic = isRuleGroupTypeIC(query);
6005
- if (newPathOrShiftDirection === "up") if (pathsAreEqual(currentPath, [0])) return currentPath;
6006
- else if (currentPath.at(-1) === 0) {
6007
- const parentPath = getParentPath(currentPath);
6008
- return [...getParentPath(parentPath), Math.max(0, parentPath.at(-1) - (ic ? 1 : 0))];
6009
- } else {
6010
- const evaluationPath = [...getParentPath(currentPath), Math.max(0, currentPath.at(-1) - (ic ? 2 : 1))];
6011
- const entityAtTarget = findPath(evaluationPath, query);
6012
- if (isRuleGroup(entityAtTarget)) return [...evaluationPath, entityAtTarget.rules.length];
6013
- else return [...getParentPath(currentPath), Math.max(0, currentPath.at(-1) - (ic ? 3 : 1))];
6014
- }
6015
- else if (newPathOrShiftDirection === "down") if (pathsAreEqual([query.rules.length - 1], currentPath)) return currentPath;
6016
- else if (currentPath.at(-1) === findPath(getParentPath(currentPath), query).rules.length - 1) {
6017
- const parentPath = getParentPath(currentPath);
6018
- return [...getParentPath(parentPath), parentPath.at(-1) + 1];
6019
- } else {
6020
- const evaluationPath = [...getParentPath(currentPath), currentPath.at(-1) + (ic ? 2 : 1)];
6021
- const entityToEvaluate = findPath(evaluationPath, query);
6022
- if (isRuleGroup(entityToEvaluate)) return [...evaluationPath, 0];
6023
- else return [...getParentPath(currentPath), currentPath.at(-1) + (ic ? 3 : 2)];
6850
+ if (newPathOrShiftDirection === "up") {
6851
+ if (pathsAreEqual(currentPath, [0])) return currentPath;
6852
+ else if (currentPath.at(-1) === 0) {
6853
+ const parentPath = getParentPath(currentPath);
6854
+ return [...getParentPath(parentPath), Math.max(0, parentPath.at(-1) - (ic ? 1 : 0))];
6855
+ } else {
6856
+ const evaluationPath = [...getParentPath(currentPath), Math.max(0, currentPath.at(-1) - (ic ? 2 : 1))];
6857
+ const entityAtTarget = findPath(evaluationPath, query);
6858
+ if (isRuleGroup(entityAtTarget)) return [...evaluationPath, entityAtTarget.rules.length];
6859
+ else return [...getParentPath(currentPath), Math.max(0, currentPath.at(-1) - (ic ? 3 : 1))];
6860
+ }
6861
+ } else if (newPathOrShiftDirection === "down") {
6862
+ if (pathsAreEqual([query.rules.length - 1], currentPath)) return currentPath;
6863
+ else if (currentPath.at(-1) === findPath(getParentPath(currentPath), query).rules.length - 1) {
6864
+ const parentPath = getParentPath(currentPath);
6865
+ return [...getParentPath(parentPath), parentPath.at(-1) + 1];
6866
+ } else {
6867
+ const evaluationPath = [...getParentPath(currentPath), currentPath.at(-1) + (ic ? 2 : 1)];
6868
+ const entityToEvaluate = findPath(evaluationPath, query);
6869
+ if (isRuleGroup(entityToEvaluate)) return [...evaluationPath, 0];
6870
+ else return [...getParentPath(currentPath), currentPath.at(-1) + (ic ? 3 : 2)];
6871
+ }
6024
6872
  }
6025
6873
  return currentPath;
6026
6874
  };
@@ -6032,7 +6880,7 @@ const getNextPath = (query, currentPath, newPathOrShiftDirection) => {
6032
6880
  *
6033
6881
  * @group Query Tools
6034
6882
  */
6035
- const move = (query, oldPathOrID, newPath, options = {}) => (0, immer.produce)(query, (q) => moveInPlace(q, oldPathOrID, newPath, options));
6883
+ const move = (query, oldPathOrID, newPath, options = {}) => producerFor(options.freeze)(query, (q) => moveInPlace(q, oldPathOrID, newPath, options));
6036
6884
  /**
6037
6885
  * Moves a rule or group from one path to another in place.
6038
6886
  * In the options parameter, pass `{ clone: true }` to copy instead of move.
@@ -6128,9 +6976,10 @@ const moveInPlace = (query, oldPathOrID, newPath, options = {}) => {
6128
6976
  */
6129
6977
  const insertRuleOrGroup = (...args) => parentToInsertInto.rules.splice(spliceIndex, 0, ...args);
6130
6978
  if (parentToInsertInto.rules.length === 0 || !independentCombinators) insertRuleOrGroup(ruleOrGroup);
6131
- else if (newIndex === 0) if (ruleToRemoveIndex === 0 && oldNextCombinator) insertRuleOrGroup(ruleOrGroup, oldNextCombinator);
6132
- else insertRuleOrGroup(ruleOrGroup, parentToInsertInto.rules[1] ?? oldPrevCombinator ?? getFirstOption(combinators));
6133
- else if (oldPrevCombinator) insertRuleOrGroup(oldPrevCombinator, ruleOrGroup);
6979
+ else if (newIndex === 0) {
6980
+ if (ruleToRemoveIndex === 0 && oldNextCombinator) insertRuleOrGroup(ruleOrGroup, oldNextCombinator);
6981
+ else insertRuleOrGroup(ruleOrGroup, parentToInsertInto.rules[1] ?? oldPrevCombinator ?? getFirstOption(combinators));
6982
+ } else if (oldPrevCombinator) insertRuleOrGroup(oldPrevCombinator, ruleOrGroup);
6134
6983
  else insertRuleOrGroup(parentToInsertInto.rules[spliceIndex - 2] ?? oldNextCombinator ?? getFirstOption(combinators), ruleOrGroup);
6135
6984
  return query;
6136
6985
  };
@@ -6141,7 +6990,7 @@ const moveInPlace = (query, oldPathOrID, newPath, options = {}) => {
6141
6990
  *
6142
6991
  * @group Query Tools
6143
6992
  */
6144
- const insert = (query, ruleOrGroup, path, options = {}) => (0, immer.produce)(query, (q) => insertInPlace(q, ruleOrGroup, path, options));
6993
+ const insert = (query, ruleOrGroup, path, options = {}) => producerFor(options.freeze)(query, (q) => insertInPlace(q, ruleOrGroup, path, options));
6145
6994
  /**
6146
6995
  * Inserts a rule or group into a query in place.
6147
6996
  *
@@ -6196,9 +7045,10 @@ const insertInPlace = (query, ruleOrGroup, path, options = {}) => {
6196
7045
  const insertRuleOrGroup = (idx, ...args) => parentToInsertInto.rules.splice(idx, replace ? args.length : 0, ...args);
6197
7046
  if (parentToInsertInto.rules.length === 0 || !independentCombinators) insertRuleOrGroup(newIndex, rorg);
6198
7047
  else if (replace && independentCombinators) insertRuleOrGroup(newIndex + newIndex % 2, rorg);
6199
- else if (newIndex === 0) if (rorg.path?.at(-1) === 0 && combinatorSucceeding) insertRuleOrGroup(newIndex, rorg, combinatorSucceeding);
6200
- else insertRuleOrGroup(newIndex, rorg, parentToInsertInto.rules[1] ?? combinatorPreceding ?? getFirstOption(combinators));
6201
- else {
7048
+ else if (newIndex === 0) {
7049
+ if (rorg.path?.at(-1) === 0 && combinatorSucceeding) insertRuleOrGroup(newIndex, rorg, combinatorSucceeding);
7050
+ else insertRuleOrGroup(newIndex, rorg, parentToInsertInto.rules[1] ?? combinatorPreceding ?? getFirstOption(combinators));
7051
+ } else {
6202
7052
  const normalizedNewIndex = newIndex % 2 === 0 ? newIndex - 1 : newIndex;
6203
7053
  if (combinatorPreceding) insertRuleOrGroup(normalizedNewIndex, combinatorPreceding, rorg);
6204
7054
  else insertRuleOrGroup(normalizedNewIndex, parentToInsertInto.rules[normalizedNewIndex - 2] ?? combinatorSucceeding ?? getFirstOption(combinators), rorg);
@@ -6214,7 +7064,7 @@ const insertInPlace = (query, ruleOrGroup, path, options = {}) => {
6214
7064
  *
6215
7065
  * @group Query Tools
6216
7066
  */
6217
- const group = (query, sourcePathOrID, targetPathOrID, options = {}) => (0, immer.produce)(query, (q) => groupInPlace(q, sourcePathOrID, targetPathOrID, options));
7067
+ const group = (query, sourcePathOrID, targetPathOrID, options = {}) => producerFor(options.freeze)(query, (q) => groupInPlace(q, sourcePathOrID, targetPathOrID, options));
6218
7068
  /**
6219
7069
  * Creates a new group at a target path with its `rules` array containing the current
6220
7070
  * objects at the target path and the source path in place.
@@ -6342,7 +7192,7 @@ const groupInPlace = (query, sourcePathOrID, targetPathOrID, options = {}) => {
6342
7192
  * @group Query Tools
6343
7193
  */
6344
7194
  const createQueryActions = (config = {}) => {
6345
- const { qbId, combinators, idGenerator, maxLevels = Infinity, queryDisabled, respectDisabled = true, disabledPaths, resetOnFieldChange, resetOnOperatorChange, getRuleDefaultOperator, getValueSources, getRuleDefaultValue, getMatchModes, onAddRule, onAddGroup, onRemove, onMoveRule, onMoveGroup, onGroupRule, onGroupGroup, onLog } = config;
7195
+ const { qbId, combinators, idGenerator, maxLevels = Infinity, queryDisabled, respectDisabled = true, disabledPaths, resetOnFieldChange, resetOnOperatorChange, getRuleDefaultOperator, getValueSources, getRuleDefaultValue, getMatchModes, freeze, onAddRule, onAddGroup, onRemove, onMoveRule, onMoveGroup, onGroupRule, onGroupGroup, onLog } = config;
6346
7196
  const log = (payload) => onLog?.({
6347
7197
  qbId,
6348
7198
  ...payload
@@ -6379,7 +7229,8 @@ const createQueryActions = (config = {}) => {
6379
7229
  const newQuery = add(query, newRule, parentPath, {
6380
7230
  combinators,
6381
7231
  combinatorPreceding: newRule.combinatorPreceding,
6382
- idGenerator
7232
+ idGenerator,
7233
+ freeze
6383
7234
  });
6384
7235
  log({
6385
7236
  type: LogType.add,
@@ -6415,7 +7266,8 @@ const createQueryActions = (config = {}) => {
6415
7266
  const newQuery = add(query, newGroup, parentPath, {
6416
7267
  combinators,
6417
7268
  combinatorPreceding: newGroup.combinatorPreceding ?? void 0,
6418
- idGenerator
7269
+ idGenerator,
7270
+ freeze
6419
7271
  });
6420
7272
  log({
6421
7273
  type: LogType.add,
@@ -6443,7 +7295,8 @@ const createQueryActions = (config = {}) => {
6443
7295
  getRuleDefaultOperator,
6444
7296
  getValueSources,
6445
7297
  getRuleDefaultValue,
6446
- getMatchModes
7298
+ getMatchModes,
7299
+ freeze
6447
7300
  });
6448
7301
  log({
6449
7302
  type: LogType.update,
@@ -6476,7 +7329,7 @@ const createQueryActions = (config = {}) => {
6476
7329
  });
6477
7330
  return;
6478
7331
  }
6479
- const newQuery = remove(query, path);
7332
+ const newQuery = remove(query, path, { freeze });
6480
7333
  log({
6481
7334
  type: LogType.remove,
6482
7335
  query,
@@ -6501,7 +7354,8 @@ const createQueryActions = (config = {}) => {
6501
7354
  const nextQuery = move(query, oldPath, newPath, {
6502
7355
  clone,
6503
7356
  combinators,
6504
- idGenerator
7357
+ idGenerator,
7358
+ freeze
6505
7359
  });
6506
7360
  const isGroup = isRuleGroup(ruleOrGroup);
6507
7361
  const callback = isGroup ? onMoveGroup : onMoveRule;
@@ -6547,7 +7401,8 @@ const createQueryActions = (config = {}) => {
6547
7401
  const nextQuery = group(query, sourcePath, targetPath, {
6548
7402
  clone,
6549
7403
  combinators,
6550
- idGenerator
7404
+ idGenerator,
7405
+ freeze
6551
7406
  });
6552
7407
  const isGroup = isRuleGroup(ruleOrGroup);
6553
7408
  const callback = isGroup ? onGroupGroup : onGroupRule;
@@ -6591,10 +7446,12 @@ const createQueryActions = (config = {}) => {
6591
7446
  */
6592
7447
  const createRule = ({ fields, getDefaultField, getRuleDefaultOperator, getValueSources, getMatchModes, getRuleDefaultValue, idGenerator = generateID }) => {
6593
7448
  let field = getFirstOption(fields) ?? "";
6594
- if (getDefaultField) if (typeof getDefaultField === "function") {
6595
- const defaultField = getDefaultField(fields);
6596
- if (defaultField) field = defaultField;
6597
- } else field = getDefaultField;
7449
+ if (getDefaultField) {
7450
+ if (typeof getDefaultField === "function") {
7451
+ const defaultField = getDefaultField(fields);
7452
+ if (defaultField) field = defaultField;
7453
+ } else field = getDefaultField;
7454
+ }
6598
7455
  const fieldData = getOption(fields, field);
6599
7456
  const operator = getRuleDefaultOperator(field);
6600
7457
  const valueSource = getFirstOption(getValueSources(field, operator, { fieldData })) ?? "value";
@@ -6641,92 +7498,8 @@ const createRuleGroup = ({ combinators, addRuleToNewGroups, createRule: createRu
6641
7498
  };
6642
7499
  };
6643
7500
  //#endregion
6644
- //#region src/utils/signature.ts
6645
- /**
6646
- * Signature returned when a change alters the _shape_ of the query—rules or groups added,
6647
- * removed, moved, or reordered—rather than the properties of a single node. Structural changes
6648
- * never coalesce with each other, so each one produces its own history entry.
6649
- */
6650
- const structuralSignature = "~structural";
6651
- /**
6652
- * Signature returned when two queries differ by reference only, with no observable difference
6653
- * in their properties.
6654
- */
6655
- const unchangedSignature = "";
6656
- /**
6657
- * Returns the list of property names that differ between two nodes, ignoring `rules` (which is
6658
- * compared structurally by the caller).
6659
- */
6660
- const changedProps = (prev, next) => {
6661
- const keys = /* @__PURE__ */ new Set([...Object.keys(prev), ...Object.keys(next)]);
6662
- keys.delete("rules");
6663
- const changed = [];
6664
- for (const key of keys) if (!Object.is(prev[key], next[key])) changed.push(key);
6665
- return changed.toSorted();
6666
- };
6667
- const signatureOfNode = (prev, next, parentId, index) => {
6668
- if (prev === next) return "";
6669
- if (typeof prev === "string" || typeof next === "string") return typeof prev === typeof next ? `${parentId}:combinator[${index}]` : structuralSignature;
6670
- const prevIsGroup = isRuleGroup(prev);
6671
- const nextIsGroup = isRuleGroup(next);
6672
- if (prevIsGroup !== nextIsGroup) return structuralSignature;
6673
- if (!prevIsGroup || !nextIsGroup) {
6674
- const props = changedProps(prev, next);
6675
- return props.length === 0 ? "" : `${next.id ?? ""}:${props.join(",")}`;
6676
- }
6677
- if (prev.rules.length !== next.rules.length) return structuralSignature;
6678
- const ownProps = changedProps(prev, next);
6679
- let changedIndex = -1;
6680
- for (const [i, prevRule] of prev.rules.entries()) if (prevRule !== next.rules[i]) {
6681
- if (changedIndex !== -1) return structuralSignature;
6682
- changedIndex = i;
6683
- }
6684
- if (changedIndex === -1) return ownProps.length === 0 ? "" : `${next.id ?? ""}:${ownProps.join(",")}`;
6685
- if (ownProps.length > 0) return structuralSignature;
6686
- return signatureOfNode(prev.rules[changedIndex], next.rules[changedIndex], next.id ?? "", changedIndex);
6687
- };
6688
- /**
6689
- * Describes _what changed_ between two versions of a query as a short string, so that
6690
- * consecutive edits to the same property of the same rule (e.g. typing in a value editor) can
6691
- * be recognized and coalesced into a single history entry.
6692
- *
6693
- * Returns {@link structuralSignature} for changes to the shape of the query, {@link unchangedSignature} when the
6694
- * two queries differ by reference only, and `"<id>:<props>"` otherwise.
6695
- *
6696
- * The walk prunes on reference equality, so it costs O(depth) rather than O(size) for the
6697
- * single-node edits that make up the overwhelming majority of changes.
6698
- *
6699
- * Note that this relies on the structural sharing that Immer—and therefore every query
6700
- * produced by RQB's own update functions—guarantees: nodes that did not change keep their
6701
- * identity. A query that has been wholly rebuilt (deep-cloned, round-tripped through JSON,
6702
- * re-parsed from a string) shares no identity with its predecessor, so it is reported as
6703
- * {@link structuralSignature} and will not coalesce. That is a graceful degradation—every change simply
6704
- * gets its own history entry—but controlled components that clone the query on every change
6705
- * will not benefit from coalescing.
6706
- */
6707
- const signatureOf = (prev, next) => signatureOfNode(prev, next, "", -1);
6708
- //#endregion
6709
7501
  //#region src/utils/QueryManager.ts
6710
7502
  /**
6711
- * Abort reasons that {@link QueryManager}'s `strict` mode treats as errors. The remaining
6712
- * reasons—`"same-location"` and `"no-change"`—describe valid operations that had nothing to do,
6713
- * so they are reported to `onInvalidTarget` but never throw.
6714
- *
6715
- * @group Query Tools
6716
- */
6717
- const strictAbortReasons = [
6718
- "target-not-found",
6719
- "parent-not-found",
6720
- "parent-not-a-group",
6721
- "destination-not-found",
6722
- "root-not-allowed",
6723
- "not-a-combinator-slot",
6724
- "target-disabled",
6725
- "parent-disabled",
6726
- "max-levels-exceeded"
6727
- ];
6728
- const strictAbortReasonSet = new Set(strictAbortReasons);
6729
- /**
6730
7503
  * Thrown by {@link QueryManager} methods in `strict` mode when an operation cannot be carried
6731
7504
  * out because its target could not be used.
6732
7505
  *
@@ -6746,6 +7519,57 @@ var QueryManagerError = class QueryManagerError extends Error {
6746
7519
  }
6747
7520
  };
6748
7521
  /**
7522
+ * Key of the single own property holding every {@link QueryManager} instance's state.
7523
+ *
7524
+ * `#private` fields are unreachable from a `Proxy`: inside a method invoked through one, `this`
7525
+ * _is_ the proxy, and the private brand lives on the target, so every access throws
7526
+ * `TypeError: Cannot read private member`. Frameworks wrap objects in proxies routinely — Vue's
7527
+ * `reactive()`, and by extension Vue Test Utils' handling of mount props, do it implicitly — so
7528
+ * consumers hit that failure by accident rather than by choice.
7529
+ *
7530
+ * A `WeakMap` keyed by instance does not fix it either, for the same reason: `weakMap.get(this)`
7531
+ * misses the entry stored under the target. An ordinary own property is the only mechanism that
7532
+ * reads correctly through a proxy, because the `get` trap forwards to the target.
7533
+ *
7534
+ * The symbol is module-private and never exported. Reaching into the state bag (via
7535
+ * `Object.getOwnPropertySymbols`, say) is not a supported access path and its contents may change
7536
+ * in any release.
7537
+ */
7538
+ const kState = Symbol("QueryManager.state");
7539
+ const queryChange = (0, immer.freeze)({
7540
+ query: true,
7541
+ config: false
7542
+ });
7543
+ const configChange = (0, immer.freeze)({
7544
+ query: false,
7545
+ config: true
7546
+ });
7547
+ const queryAndConfigChange = (0, immer.freeze)({
7548
+ query: true,
7549
+ config: true
7550
+ });
7551
+ /**
7552
+ * Builds the state bag. The option-derived members are left unset for `applyOptions` to fill in,
7553
+ * hence the assertion.
7554
+ */
7555
+ const createManagerState = () => {
7556
+ const state = {
7557
+ listeners: /* @__PURE__ */ new Set(),
7558
+ configVersion: 0,
7559
+ past: [],
7560
+ future: [],
7561
+ lastAt: 0,
7562
+ batchDepth: 0,
7563
+ historyBypassed: false,
7564
+ pendingConfigNotify: false
7565
+ };
7566
+ Object.defineProperty(state, "__v_skip", {
7567
+ value: true,
7568
+ enumerable: false
7569
+ });
7570
+ return state;
7571
+ };
7572
+ /**
6749
7573
  * Stateful wrapper around the {@link add}/{@link remove}/{@link update}/{@link move}/
6750
7574
  * {@link insert}/{@link group} query tools, plus rule/group factories, {@link defaultValidator
6751
7575
  * validation}, and {@link formatQuery formatting}.
@@ -6753,7 +7577,8 @@ var QueryManagerError = class QueryManagerError extends Error {
6753
7577
  * The query is held internally, so each method takes the same arguments as its `queryTools`
6754
7578
  * counterpart minus the leading `query` parameter, and returns the manager itself for chaining.
6755
7579
  * Mutations use the non-`InPlace` tools, so a query previously handed out by
6756
- * {@link QueryManager.getQuery} is never modified.
7580
+ * {@link QueryManager.getQuery} is never modified. Under `freeze: false` that remains true, but
7581
+ * as a convention rather than a runtime-enforced guarantee.
6757
7582
  *
6758
7583
  * ```ts
6759
7584
  * const q = new QueryManager(undefined, { fields });
@@ -6769,194 +7594,188 @@ var QueryManagerError = class QueryManagerError extends Error {
6769
7594
  * @group Query Tools
6770
7595
  */
6771
7596
  var QueryManager = class QueryManager {
6772
- #query;
6773
- #options;
6774
- #fields;
6775
- #fieldMap;
6776
- #operators;
6777
- #combinators;
6778
- #idGenerator;
6779
- #validator;
6780
- #strict;
6781
- #respectDisabled;
6782
- #onInvalidTarget;
6783
- #listeners = /* @__PURE__ */ new Set();
6784
- #historyEnabled;
6785
- #maxHistory;
6786
- #coalesceMs;
6787
- #now;
6788
- /** Incremented by every {@link QueryManager.reconfigure} call. See `getConfigVersion`. */
6789
- #configVersion = 0;
6790
- #past = [];
6791
- #future = [];
6792
- #lastSig;
6793
- #lastAt = 0;
6794
- #batchDepth = 0;
6795
- #batchSnapshot;
6796
- /** Whether a history-stack method ran inside the batch currently in progress. */
6797
- #historyBypassed = false;
6798
7597
  /**
6799
- * The query the cached fields below were derived from. Caches are keyed on query _identity_
6800
- * rather than invalidated from {@link QueryManager.#commit} because `undo`, `redo`, and
6801
- * `batch`'s rollback all assign `#query` directly.
7598
+ * All instance state, rather than `#private` fields. See `kState` for why.
7599
+ *
7600
+ * The property itself is symbol-keyed and installed by the constructor; this accessor exists
7601
+ * because `isolatedDeclarations` cannot emit a computed property name keyed by a symbol the
7602
+ * module does not export, and exporting it would put it in the public API surface. A
7603
+ * prototype accessor is proxy-safe for the same reason the own property is: `this` is the
7604
+ * proxy, and the `get` trap forwards to the target.
6802
7605
  */
6803
- #cacheFor;
6804
- #idPathIndex;
6805
- #validation;
7606
+ get state() {
7607
+ return this[kState];
7608
+ }
6806
7609
  constructor(query, options = {}) {
6807
- this.#applyOptions(options);
6808
- this.#query = (0, immer.freeze)(query ? prepareRuleGroup(query, { idGenerator: this.#idGenerator }) : this.createRuleGroup(), true);
7610
+ Object.defineProperty(this, kState, {
7611
+ value: createManagerState(),
7612
+ enumerable: false,
7613
+ writable: false,
7614
+ configurable: false
7615
+ });
7616
+ this.applyOptions(options);
7617
+ this.state.query = this.freeze(query ? prepareRuleGroup(query, { idGenerator: this.state.idGenerator }) : this.createRuleGroup());
7618
+ }
7619
+ /**
7620
+ * Deep-freezes `x` unless the `freeze` option is `false`. Every value the manager hands out
7621
+ * directly goes through here; the shallow copies returned by {@link QueryManager.getOptions}
7622
+ * and passed to `applyOptions` are frozen unconditionally instead.
7623
+ */
7624
+ freeze(x) {
7625
+ return this.state.freezeEnabled ? (0, immer.freeze)(x, true) : x;
6809
7626
  }
6810
7627
  /**
6811
- * Assigns `#options` and every field derived from it. Shared by the constructor and
7628
+ * Assigns `options` and every field derived from it. Shared by the constructor and
6812
7629
  * {@link QueryManager.reconfigure}, so the two can never drift apart. Does not touch the
6813
7630
  * query, the history stacks, the caches, or the subscriber list.
6814
7631
  */
6815
- #applyOptions(options) {
6816
- this.#options = options;
6817
- this.#idGenerator = options.idGenerator ?? generateID;
6818
- this.#validator = options.validator ?? defaultValidator;
6819
- this.#strict = options.strict ?? false;
6820
- this.#respectDisabled = options.respectDisabled ?? true;
6821
- this.#onInvalidTarget = options.onInvalidTarget;
7632
+ applyOptions(options) {
7633
+ this.state.options = options;
7634
+ this.state.freezeEnabled = options.freeze ?? true;
7635
+ this.state.idGenerator = options.idGenerator ?? generateID;
7636
+ this.state.validator = options.validator ?? defaultValidator;
7637
+ this.state.strict = options.strict ?? false;
7638
+ this.state.respectDisabled = options.respectDisabled ?? true;
7639
+ this.state.onInvalidTarget = options.onInvalidTarget;
6822
7640
  const history = options.history ?? false;
6823
7641
  const historyOptions = typeof history === "object" ? history : {};
6824
- this.#historyEnabled = history !== false;
6825
- this.#maxHistory = historyOptions.maxHistory ?? 50;
6826
- this.#coalesceMs = historyOptions.coalesceMs ?? 500;
6827
- this.#now = options.now ?? Date.now;
7642
+ this.state.historyEnabled = history !== false;
7643
+ this.state.maxHistory = historyOptions.maxHistory ?? 50;
7644
+ this.state.coalesceMs = historyOptions.coalesceMs ?? 500;
7645
+ this.state.now = options.now ?? Date.now;
6828
7646
  const { optionList: fields, optionsMap: fieldMap } = prepareOptionList({
6829
7647
  optionList: options.fields,
6830
7648
  baseOption: options.baseField,
6831
7649
  autoSelectOption: options.autoSelectField,
6832
7650
  placeholder: options.translations?.fields
6833
7651
  });
6834
- this.#fields = (0, immer.freeze)(fields, true);
6835
- this.#fieldMap = (0, immer.freeze)(fieldMap, true);
6836
- this.#operators = prepareOptionList({
7652
+ this.state.fields = this.freeze(fields);
7653
+ this.state.fieldMap = this.freeze(fieldMap);
7654
+ this.state.operators = prepareOptionList({
6837
7655
  optionList: options.operators ?? defaultOperators,
6838
7656
  baseOption: options.baseOperator,
6839
7657
  labelMap: defaultOperatorLabelMap,
6840
7658
  autoSelectOption: options.autoSelectOperator,
6841
7659
  placeholder: options.translations?.operators
6842
7660
  }).optionList;
6843
- this.#combinators = (0, immer.freeze)(prepareOptionList({
7661
+ this.state.combinators = this.freeze(prepareOptionList({
6844
7662
  optionList: options.combinators ?? defaultCombinators,
6845
7663
  baseOption: options.baseCombinator
6846
- }).optionList, true);
7664
+ }).optionList);
6847
7665
  }
6848
7666
  /** Resolves the field configuration for a field name. */
6849
- #fieldData(field) {
6850
- return this.#fieldMap[field] ?? {};
7667
+ fieldData(field) {
7668
+ return this.state.fieldMap[field] ?? {};
6851
7669
  }
6852
7670
  /** Resolves the operator list for a field, mirroring `QueryBuilder`'s precedence. */
6853
- #operatorsFor(field) {
7671
+ operatorsFor(field) {
6854
7672
  return resolveOperatorList({
6855
7673
  field,
6856
- fieldData: this.#fieldData(field),
6857
- getOperators: this.#options.getOperators,
6858
- operators: this.#operators,
6859
- baseOption: this.#options.baseOperator,
6860
- autoSelectOption: this.#options.autoSelectOperator,
6861
- placeholder: this.#options.translations?.operators
7674
+ fieldData: this.fieldData(field),
7675
+ getOperators: this.state.options.getOperators,
7676
+ operators: this.state.operators,
7677
+ baseOption: this.state.options.baseOperator,
7678
+ autoSelectOption: this.state.options.autoSelectOperator,
7679
+ placeholder: this.state.options.translations?.operators
6862
7680
  });
6863
7681
  }
6864
7682
  /** Resolves the default operator for a field, mirroring `QueryBuilder`'s precedence. */
6865
- #defaultOperator(field) {
7683
+ defaultOperator(field) {
6866
7684
  return resolveDefaultOperator({
6867
7685
  field,
6868
- fieldData: this.#fieldData(field),
6869
- getDefaultOperator: this.#options.getDefaultOperator,
6870
- getOperators: (f) => this.#operatorsFor(f)
7686
+ fieldData: this.fieldData(field),
7687
+ getDefaultOperator: this.state.options.getDefaultOperator,
7688
+ getOperators: (f) => this.operatorsFor(f)
6871
7689
  });
6872
7690
  }
6873
- #valueSourcesFor(field, operator) {
6874
- return getValueSourcesUtil(this.#fieldData(field), operator, this.#options.getValueSources);
7691
+ valueSourcesFor(field, operator) {
7692
+ return getValueSourcesUtil(this.fieldData(field), operator, this.state.options.getValueSources);
6875
7693
  }
6876
- #matchModesFor(field) {
6877
- return getMatchModesUtil(this.#fieldData(field), this.#options.getMatchModes);
7694
+ matchModesFor(field) {
7695
+ return getMatchModesUtil(this.fieldData(field), this.state.options.getMatchModes);
6878
7696
  }
6879
- #valuesFor(field, operator) {
7697
+ valuesFor(field, operator) {
6880
7698
  return resolveValueList({
6881
7699
  field,
6882
7700
  operator,
6883
- fieldData: this.#fieldData(field),
6884
- getValues: this.#options.getValues,
6885
- autoSelectOption: this.#options.autoSelectValue,
6886
- placeholder: this.#options.translations?.values
7701
+ fieldData: this.fieldData(field),
7702
+ getValues: this.state.options.getValues,
7703
+ autoSelectOption: this.state.options.autoSelectValue,
7704
+ placeholder: this.state.options.translations?.values
6887
7705
  });
6888
7706
  }
6889
- #valueEditorTypeFor(field, operator) {
7707
+ valueEditorTypeFor(field, operator) {
6890
7708
  return resolveValueEditorType({
6891
7709
  field,
6892
7710
  operator,
6893
- fieldData: this.#fieldData(field),
6894
- getValueEditorType: this.#options.getValueEditorType
7711
+ fieldData: this.fieldData(field),
7712
+ getValueEditorType: this.state.options.getValueEditorType
6895
7713
  });
6896
7714
  }
6897
7715
  /** Computes the default `value` for a rule, mirroring `QueryBuilder`'s precedence. */
6898
- #defaultValue(rule) {
6899
- const { getDefaultValue, getParameters, listsAsArrays } = this.#options;
7716
+ defaultValue(rule) {
7717
+ const { getDefaultValue, getParameters, listsAsArrays } = this.state.options;
6900
7718
  return getRuleDefaultValue(rule, {
6901
- fieldData: this.#fieldData(rule.field),
6902
- fields: this.#fields,
7719
+ fieldData: this.fieldData(rule.field),
7720
+ fields: this.state.fields,
6903
7721
  listsAsArrays,
6904
- getValueEditorType: (f, o) => this.#valueEditorTypeFor(f, o),
6905
- getValues: (f, o) => this.#valuesFor(f, o),
7722
+ getValueEditorType: (f, o) => this.valueEditorTypeFor(f, o),
7723
+ getValues: (f, o) => this.valuesFor(f, o),
6906
7724
  getDefaultValue: getDefaultValue && ((r, misc) => getDefaultValue(r, misc)),
6907
- getParameters: getParameters && ((f, o, misc) => this.#parametersFor(f, o, misc))
7725
+ getParameters: getParameters && ((f, o, misc) => this.parametersFor(f, o, misc))
6908
7726
  });
6909
7727
  }
6910
7728
  /**
6911
7729
  * Resolves the parameter list for a field/operator pair, normalized the same way as every
6912
- * other option list. Shared by {@link QueryManager.#defaultValue} and
6913
- * {@link QueryManager.getRuleContext} so both see the same shape.
7730
+ * other option list. Shared by `defaultValue` and {@link QueryManager.getRuleContext} so both
7731
+ * see the same shape.
6914
7732
  */
6915
- #parametersFor(field, operator, misc) {
7733
+ parametersFor(field, operator, misc) {
6916
7734
  return prepareOptionList({
6917
- optionList: this.#options.getParameters?.(field, operator, misc) ?? [],
6918
- autoSelectOption: this.#options.autoSelectValue
7735
+ optionList: this.state.options.getParameters?.(field, operator, misc) ?? [],
7736
+ autoSelectOption: this.state.options.autoSelectValue
6919
7737
  }).optionList;
6920
7738
  }
6921
7739
  /** Defaults shared by every mutating method, overridable per call. */
6922
- #guardOptions() {
6923
- const { maxLevels } = this.#options;
7740
+ guardOptions() {
7741
+ const { maxLevels } = this.state.options;
6924
7742
  return {
7743
+ freeze: this.state.freezeEnabled,
6925
7744
  maxLevels: (maxLevels ?? 0) > 0 ? Number(maxLevels) : Infinity,
6926
- respectDisabled: this.#respectDisabled,
6927
- disabledPaths: this.#options.disabledPaths,
6928
- queryDisabled: this.#options.queryDisabled
7745
+ respectDisabled: this.state.respectDisabled,
7746
+ disabledPaths: this.state.options.disabledPaths,
7747
+ queryDisabled: this.state.options.queryDisabled
6929
7748
  };
6930
7749
  }
6931
- #toolOptions() {
7750
+ toolOptions() {
6932
7751
  return {
6933
- combinators: this.#combinators,
6934
- idGenerator: this.#idGenerator,
6935
- ...this.#guardOptions()
7752
+ combinators: this.state.combinators,
7753
+ idGenerator: this.state.idGenerator,
7754
+ ...this.guardOptions()
6936
7755
  };
6937
7756
  }
6938
7757
  /**
6939
7758
  * Builds the `onAbort` handler passed to the query tools, applying the per-call overrides on
6940
7759
  * top of the manager's own options.
6941
7760
  */
6942
- #onAbort({ strict, onInvalidTarget }) {
6943
- const strictMain = strict ?? this.#strict;
6944
- const handler = onInvalidTarget ?? this.#onInvalidTarget;
7761
+ onAbort({ strict, onInvalidTarget }) {
7762
+ const strictMain = strict ?? this.state.strict;
7763
+ const handler = onInvalidTarget ?? this.state.onInvalidTarget;
6945
7764
  return (info) => {
6946
7765
  handler?.(info);
6947
7766
  if (strictMain && strictAbortReasonSet.has(info.reason)) throw new QueryManagerError(info);
6948
7767
  };
6949
7768
  }
6950
7769
  /** Defaults for {@link update}, so resets mirror `QueryBuilder`'s behavior. */
6951
- #updateOptions() {
7770
+ updateOptions() {
6952
7771
  return {
6953
- getRuleDefaultOperator: (f) => this.#defaultOperator(f),
6954
- getRuleDefaultValue: (r) => this.#defaultValue(r),
6955
- getValueSources: (f, o) => this.#valueSourcesFor(f, o),
6956
- getMatchModes: (f) => this.#matchModesFor(f),
6957
- resetOnFieldChange: this.#options.resetOnFieldChange,
6958
- resetOnOperatorChange: this.#options.resetOnOperatorChange,
6959
- ...this.#guardOptions()
7772
+ getRuleDefaultOperator: (f) => this.defaultOperator(f),
7773
+ getRuleDefaultValue: (r) => this.defaultValue(r),
7774
+ getValueSources: (f, o) => this.valueSourcesFor(f, o),
7775
+ getMatchModes: (f) => this.matchModesFor(f),
7776
+ resetOnFieldChange: this.state.options.resetOnFieldChange,
7777
+ resetOnOperatorChange: this.state.options.resetOnOperatorChange,
7778
+ ...this.guardOptions()
6960
7779
  };
6961
7780
  }
6962
7781
  /**
@@ -6964,52 +7783,52 @@ var QueryManager = class QueryManager {
6964
7783
  * mutation funnels through here. A tool that could not resolve its target returns the same
6965
7784
  * query object, which is treated as a no-op.
6966
7785
  */
6967
- #commit(next) {
6968
- const prev = this.#query;
7786
+ commit(next) {
7787
+ const prev = this.state.query;
6969
7788
  if (prev === next) return;
6970
- this.#query = next;
6971
- if (this.#batchDepth > 0) return;
6972
- this.#record(prev, next);
6973
- this.#notify();
7789
+ this.state.query = next;
7790
+ if (this.state.batchDepth > 0) return;
7791
+ this.record(prev, next);
7792
+ this.notify(queryChange);
6974
7793
  }
6975
7794
  /**
6976
7795
  * Records a change, either as a new history entry or by absorbing it into the current one.
6977
7796
  * Mirrors the recording semantics of the `react-querybuilder/history` entry point.
6978
7797
  */
6979
- #record(prev, next) {
6980
- if (!this.#historyEnabled) return;
7798
+ record(prev, next) {
7799
+ if (!this.state.historyEnabled) return;
6981
7800
  const sig = signatureOf(prev, next);
6982
7801
  if (sig === "") return;
6983
- const now = this.#now();
6984
- if (!(sig !== "~structural" && sig === this.#lastSig && now - this.#lastAt < this.#coalesceMs)) {
6985
- this.#past.push(prev);
6986
- if (this.#past.length > this.#maxHistory) this.#past.shift();
6987
- this.#future = [];
7802
+ const now = this.state.now();
7803
+ if (!shouldCoalesce(this.state.lastSig, sig, this.state.lastAt, now, this.state.coalesceMs)) {
7804
+ this.state.past.push(prev);
7805
+ if (this.state.past.length > this.state.maxHistory) this.state.past.shift();
7806
+ this.state.future = [];
6988
7807
  }
6989
- this.#lastSig = sig;
6990
- this.#lastAt = now;
7808
+ this.state.lastSig = sig;
7809
+ this.state.lastAt = now;
6991
7810
  }
6992
- #notify() {
6993
- for (const listener of this.#listeners) listener();
7811
+ notify(change) {
7812
+ for (const listener of this.state.listeners) listener(change);
6994
7813
  }
6995
7814
  /**
6996
7815
  * Records that a history-stack method ran inside the current batch. Those methods manage
6997
- * `#past`/`#future` themselves, so the batch must not also record an entry on completion —
6998
- * doing so would push a duplicate onto `#past` and clear the redo stack that `undo` just
7816
+ * `past`/`future` themselves, so the batch must not also record an entry on completion —
7817
+ * doing so would push a duplicate onto `past` and clear the redo stack that `undo` just
6999
7818
  * populated.
7000
7819
  */
7001
- #markHistoryBypassed() {
7002
- if (this.#batchDepth > 0) this.#historyBypassed = true;
7820
+ markHistoryBypassed() {
7821
+ if (this.state.batchDepth > 0) this.state.historyBypassed = true;
7003
7822
  }
7004
7823
  /**
7005
7824
  * Discards every cached derivation when the query has been replaced since they were computed.
7006
7825
  * Called at the top of each cached reader.
7007
7826
  */
7008
- #ensureCache() {
7009
- if (this.#cacheFor === this.#query) return;
7010
- this.#cacheFor = this.#query;
7011
- this.#idPathIndex = void 0;
7012
- this.#validation = void 0;
7827
+ ensureCache() {
7828
+ if (this.state.cacheFor === this.state.query) return;
7829
+ this.state.cacheFor = this.state.query;
7830
+ this.state.idPathIndex = void 0;
7831
+ this.state.validation = void 0;
7013
7832
  }
7014
7833
  /**
7015
7834
  * The single traversal implementation. Combinator strings in independent-combinator groups
@@ -7017,7 +7836,7 @@ var QueryManager = class QueryManager {
7017
7836
  *
7018
7837
  * @yields The subtree rooted at `node`, depth-first in pre-order, starting with `node` itself.
7019
7838
  */
7020
- *#walkFrom(node, path, parent) {
7839
+ *walkFrom(node, path, parent) {
7021
7840
  yield {
7022
7841
  node,
7023
7842
  path,
@@ -7027,34 +7846,35 @@ var QueryManager = class QueryManager {
7027
7846
  const startGroup = node;
7028
7847
  for (const [index, child] of startGroup.rules.entries()) {
7029
7848
  if (typeof child === "string") continue;
7030
- yield* this.#walkFrom(child, [...path, index], startGroup);
7849
+ yield* this.walkFrom(child, [...path, index], startGroup);
7031
7850
  }
7032
7851
  }
7033
7852
  /** Builds (once per query) the `id` to {@link Path} index backing `findID`/`getPathOfID`. */
7034
- #index() {
7035
- this.#ensureCache();
7036
- if (!this.#idPathIndex) {
7853
+ index() {
7854
+ this.ensureCache();
7855
+ if (!this.state.idPathIndex) {
7037
7856
  const index = /* @__PURE__ */ new Map();
7038
- for (const { node, path } of this.#walkFrom(this.#query, [], null)) if (node.id !== void 0 && !index.has(node.id)) index.set(node.id, path);
7039
- this.#idPathIndex = index;
7857
+ for (const { node, path } of this.walkFrom(this.state.query, [], null)) if (node.id !== void 0 && !index.has(node.id)) index.set(node.id, path);
7858
+ this.state.idPathIndex = index;
7040
7859
  }
7041
- return this.#idPathIndex;
7860
+ return this.state.idPathIndex;
7042
7861
  }
7043
7862
  /** Resolves a path or `id` to a path, or `null` when the `id` isn't present. */
7044
- #toPath(pathOrID) {
7045
- return typeof pathOrID === "string" ? this.#index().get(pathOrID) ?? null : pathOrID;
7863
+ toPath(pathOrID) {
7864
+ return typeof pathOrID === "string" ? this.index().get(pathOrID) ?? null : pathOrID;
7046
7865
  }
7047
7866
  /**
7048
- * The current query. The returned object is frozen and structurally shared, so it is safe to
7049
- * retain and compare by reference to detect changes.
7867
+ * The current query. The returned object is structurally shared, so it is safe to retain and
7868
+ * compare by reference to detect changes. It is also frozen unless the `freeze` option is
7869
+ * `false`.
7050
7870
  *
7051
7871
  * Like {@link QueryManager.subscribe}, this method is bound to the instance, so it can be
7052
7872
  * passed as a bare reference (e.g. as the `getSnapshot` argument to `useSyncExternalStore`).
7053
7873
  */
7054
- getQuery = () => this.#query;
7874
+ getQuery = () => this.state.query;
7055
7875
  /** Replaces the current query, ensuring every rule and group has an `id`. */
7056
7876
  setQuery(query) {
7057
- this.#commit((0, immer.freeze)(prepareRuleGroup(query, { idGenerator: this.#idGenerator }), true));
7877
+ this.commit(this.freeze(prepareRuleGroup(query, { idGenerator: this.state.idGenerator })));
7058
7878
  return this;
7059
7879
  }
7060
7880
  /**
@@ -7063,13 +7883,13 @@ var QueryManager = class QueryManager {
7063
7883
  */
7064
7884
  createRule() {
7065
7885
  return createRule({
7066
- fields: this.#fields,
7067
- getDefaultField: this.#options.getDefaultField,
7068
- getRuleDefaultOperator: (f) => this.#defaultOperator(f),
7069
- getValueSources: (f, o) => this.#valueSourcesFor(f, o),
7070
- getMatchModes: (f) => this.#matchModesFor(f),
7071
- getRuleDefaultValue: (r) => this.#defaultValue(r),
7072
- idGenerator: this.#idGenerator
7886
+ fields: this.state.fields,
7887
+ getDefaultField: this.state.options.getDefaultField,
7888
+ getRuleDefaultOperator: (f) => this.defaultOperator(f),
7889
+ getValueSources: (f, o) => this.valueSourcesFor(f, o),
7890
+ getMatchModes: (f) => this.matchModesFor(f),
7891
+ getRuleDefaultValue: (r) => this.defaultValue(r),
7892
+ idGenerator: this.state.idGenerator
7073
7893
  });
7074
7894
  }
7075
7895
  /**
@@ -7078,10 +7898,10 @@ var QueryManager = class QueryManager {
7078
7898
  */
7079
7899
  createRuleGroup(independentCombinators) {
7080
7900
  return createRuleGroup({
7081
- combinators: this.#combinators,
7082
- addRuleToNewGroups: this.#options.addRuleToNewGroups,
7901
+ combinators: this.state.combinators,
7902
+ addRuleToNewGroups: this.state.options.addRuleToNewGroups,
7083
7903
  createRule: () => this.createRule(),
7084
- idGenerator: this.#idGenerator
7904
+ idGenerator: this.state.idGenerator
7085
7905
  }, independentCombinators);
7086
7906
  }
7087
7907
  /**
@@ -7090,10 +7910,10 @@ var QueryManager = class QueryManager {
7090
7910
  */
7091
7911
  add(ruleOrGroup, parentPathOrID = [], options = {}) {
7092
7912
  const { strict, onInvalidTarget, ...toolOptions } = options;
7093
- this.#commit(add(this.#query, ruleOrGroup, parentPathOrID, {
7094
- ...this.#toolOptions(),
7913
+ this.commit(add(this.state.query, ruleOrGroup, parentPathOrID, {
7914
+ ...this.toolOptions(),
7095
7915
  ...toolOptions,
7096
- onAbort: this.#onAbort({
7916
+ onAbort: this.onAbort({
7097
7917
  strict,
7098
7918
  onInvalidTarget
7099
7919
  })
@@ -7103,10 +7923,10 @@ var QueryManager = class QueryManager {
7103
7923
  /** Removes the rule or group at the given path or `id`. The root group cannot be removed. */
7104
7924
  remove(pathOrID, options = {}) {
7105
7925
  const { strict, onInvalidTarget, ...toolOptions } = options;
7106
- this.#commit(remove(this.#query, pathOrID, {
7107
- ...this.#guardOptions(),
7926
+ this.commit(remove(this.state.query, pathOrID, {
7927
+ ...this.guardOptions(),
7108
7928
  ...toolOptions,
7109
- onAbort: this.#onAbort({
7929
+ onAbort: this.onAbort({
7110
7930
  strict,
7111
7931
  onInvalidTarget
7112
7932
  })
@@ -7123,23 +7943,23 @@ var QueryManager = class QueryManager {
7123
7943
  ];
7124
7944
  const { strict, onInvalidTarget, ...toolOptions } = args[optionsIndex] ?? {};
7125
7945
  args[optionsIndex] = {
7126
- ...this.#updateOptions(),
7946
+ ...this.updateOptions(),
7127
7947
  ...toolOptions,
7128
- onAbort: this.#onAbort({
7948
+ onAbort: this.onAbort({
7129
7949
  strict,
7130
7950
  onInvalidTarget
7131
7951
  })
7132
7952
  };
7133
- this.#commit(update(this.#query, ...args.slice(0, optionsIndex + 1)));
7953
+ this.commit(update(this.state.query, ...args.slice(0, optionsIndex + 1)));
7134
7954
  return this;
7135
7955
  }
7136
7956
  /** Moves the rule or group at `oldPathOrID` to `newPath`, or shifts it `'up'`/`'down'`. */
7137
7957
  move(oldPathOrID, newPath, options = {}) {
7138
7958
  const { strict, onInvalidTarget, ...toolOptions } = options;
7139
- this.#commit(move(this.#query, oldPathOrID, newPath, {
7140
- ...this.#toolOptions(),
7959
+ this.commit(move(this.state.query, oldPathOrID, newPath, {
7960
+ ...this.toolOptions(),
7141
7961
  ...toolOptions,
7142
- onAbort: this.#onAbort({
7962
+ onAbort: this.onAbort({
7143
7963
  strict,
7144
7964
  onInvalidTarget
7145
7965
  })
@@ -7152,10 +7972,10 @@ var QueryManager = class QueryManager {
7152
7972
  */
7153
7973
  insert(ruleOrGroup, path, options = {}) {
7154
7974
  const { strict, onInvalidTarget, ...toolOptions } = options;
7155
- this.#commit(insert(this.#query, ruleOrGroup, path, {
7156
- ...this.#toolOptions(),
7975
+ this.commit(insert(this.state.query, ruleOrGroup, path, {
7976
+ ...this.toolOptions(),
7157
7977
  ...toolOptions,
7158
- onAbort: this.#onAbort({
7978
+ onAbort: this.onAbort({
7159
7979
  strict,
7160
7980
  onInvalidTarget
7161
7981
  })
@@ -7168,10 +7988,10 @@ var QueryManager = class QueryManager {
7168
7988
  */
7169
7989
  group(sourcePathOrID, targetPathOrID, options = {}) {
7170
7990
  const { strict, onInvalidTarget, ...toolOptions } = options;
7171
- this.#commit(group(this.#query, sourcePathOrID, targetPathOrID, {
7172
- ...this.#toolOptions(),
7991
+ this.commit(group(this.state.query, sourcePathOrID, targetPathOrID, {
7992
+ ...this.toolOptions(),
7173
7993
  ...toolOptions,
7174
- onAbort: this.#onAbort({
7994
+ onAbort: this.onAbort({
7175
7995
  strict,
7176
7996
  onInvalidTarget
7177
7997
  })
@@ -7184,7 +8004,7 @@ var QueryManager = class QueryManager {
7184
8004
  * defaults filled in for options that were never provided.
7185
8005
  */
7186
8006
  getOptions() {
7187
- return (0, immer.freeze)({ ...this.#options });
8007
+ return (0, immer.freeze)({ ...this.state.options });
7188
8008
  }
7189
8009
  /**
7190
8010
  * Updates the manager's configuration in place, keeping the current query, the undo/redo
@@ -7207,24 +8027,37 @@ var QueryManager = class QueryManager {
7207
8027
  * re-normalize.
7208
8028
  *
7209
8029
  * History options are honored immediately: lowering `maxHistory` trims the undo stack, and
7210
- * turning history off clears both stacks. Subscribers are notified once, and
7211
- * {@link QueryManager.getConfigVersion} is incremented, even inside a
7212
- * {@link QueryManager.batch batch} — configuration is not part of a batch's rollback.
8030
+ * turning history off clears both stacks.
8031
+ *
8032
+ * A call that resolves to the configuration already in effect is a **no-op**: nothing is
8033
+ * re-derived, {@link QueryManager.getConfigVersion} does not change, and subscribers are not
8034
+ * notified. Equality is structural for data and by identity for functions
8035
+ * ({@link optionsEqual}), so a caller that rebuilds its options object on every render — which
8036
+ * every framework adapter does — does not force a reconfigure as long as the data is the same.
8037
+ * Rebuilding a callback per render _does_ count as a change; memoize it to avoid that.
8038
+ *
8039
+ * Otherwise subscribers are notified once and `getConfigVersion` is incremented. Inside a
8040
+ * {@link QueryManager.batch batch} the options are still applied immediately — configuration
8041
+ * is not part of a batch's rollback — but the notification is deferred and merged into the
8042
+ * batch's single notification.
7213
8043
  */
7214
8044
  reconfigure(options, config) {
7215
- this.#applyOptions((0, immer.freeze)(config?.replace ? { ...options } : {
7216
- ...this.#options,
8045
+ const merged = config?.replace ? { ...options } : {
8046
+ ...this.state.options,
7217
8047
  ...options
7218
- }));
7219
- this.#validation = void 0;
7220
- this.#reconcileHistoryConfig();
7221
- ++this.#configVersion;
7222
- this.#notify();
8048
+ };
8049
+ if (optionsEqual(this.state.options, merged)) return this;
8050
+ this.applyOptions((0, immer.freeze)(merged));
8051
+ this.state.validation = void 0;
8052
+ this.reconcileHistoryConfig();
8053
+ ++this.state.configVersion;
8054
+ if (this.state.batchDepth > 0) this.state.pendingConfigNotify = true;
8055
+ else this.notify(configChange);
7223
8056
  return this;
7224
8057
  }
7225
8058
  /**
7226
8059
  * Brings the history stacks in line with the current history configuration. Unlike
7227
- * {@link QueryManager.clearHistory}, this does _not_ set `#historyBypassed`: it reflects a
8060
+ * {@link QueryManager.clearHistory}, this does _not_ set `historyBypassed`: it reflects a
7228
8061
  * configuration change rather than a deliberate history repositioning, so a later mutation in
7229
8062
  * the same batch must still be recorded normally.
7230
8063
  *
@@ -7232,13 +8065,13 @@ var QueryManager = class QueryManager {
7232
8065
  * {@link QueryManager.batch batch} restores its snapshot, since that snapshot predates the
7233
8066
  * configuration change (configuration is not part of a batch's rollback).
7234
8067
  */
7235
- #reconcileHistoryConfig() {
7236
- if (this.#historyEnabled) {
7237
- if (this.#past.length > this.#maxHistory) this.#past.splice(0, this.#past.length - this.#maxHistory);
8068
+ reconcileHistoryConfig() {
8069
+ if (this.state.historyEnabled) {
8070
+ if (this.state.past.length > this.state.maxHistory) this.state.past.splice(0, this.state.past.length - this.state.maxHistory);
7238
8071
  } else {
7239
- this.#past = [];
7240
- this.#future = [];
7241
- this.#lastSig = void 0;
8072
+ this.state.past = [];
8073
+ this.state.future = [];
8074
+ this.state.lastSig = void 0;
7242
8075
  }
7243
8076
  }
7244
8077
  /**
@@ -7250,7 +8083,7 @@ var QueryManager = class QueryManager {
7250
8083
  * {@link QueryManager.subscribe}. The `useQueryManager` hook from `react-querybuilder`
7251
8084
  * already does this.
7252
8085
  */
7253
- getConfigVersion = () => this.#configVersion;
8086
+ getConfigVersion = () => this.state.configVersion;
7254
8087
  /**
7255
8088
  * Creates an independent manager with the same configuration and the current query.
7256
8089
  *
@@ -7262,8 +8095,8 @@ var QueryManager = class QueryManager {
7262
8095
  * is useful when both queries will be used together (e.g. inserted into the same tree).
7263
8096
  */
7264
8097
  clone(options) {
7265
- const query = options?.regenerateIDs ? regenerateIDs(this.#query, { idGenerator: this.#idGenerator }) : this.#query;
7266
- return new QueryManager(query, this.#options);
8098
+ const query = options?.regenerateIDs ? regenerateIDs(this.state.query, { idGenerator: this.state.idGenerator }) : this.state.query;
8099
+ return new QueryManager(query, this.state.options);
7267
8100
  }
7268
8101
  /**
7269
8102
  * Registers a listener called after every change to the query, and returns a function that
@@ -7279,11 +8112,15 @@ var QueryManager = class QueryManager {
7279
8112
  * ```
7280
8113
  *
7281
8114
  * In React, prefer the `useQueryManager` hook from `react-querybuilder`, which wraps this.
8115
+ *
8116
+ * The listener receives a {@link SubscriptionChange} describing what changed. It is optional:
8117
+ * a zero-argument listener — including `useSyncExternalStore`'s `onStoreChange` — is still a
8118
+ * valid listener and behaves as it always has.
7282
8119
  */
7283
8120
  subscribe = (listener) => {
7284
- this.#listeners.add(listener);
8121
+ this.state.listeners.add(listener);
7285
8122
  return () => {
7286
- this.#listeners.delete(listener);
8123
+ this.state.listeners.delete(listener);
7287
8124
  };
7288
8125
  };
7289
8126
  /**
@@ -7301,75 +8138,78 @@ var QueryManager = class QueryManager {
7301
8138
  * its own, leaving the stacks exactly as those methods left them.
7302
8139
  */
7303
8140
  batch(fn) {
7304
- this.#batchDepth++;
7305
- if (this.#batchDepth === 1) this.#batchSnapshot = {
7306
- query: this.#query,
7307
- past: [...this.#past],
7308
- future: [...this.#future],
7309
- lastSig: this.#lastSig,
7310
- lastAt: this.#lastAt
8141
+ this.state.batchDepth++;
8142
+ if (this.state.batchDepth === 1) this.state.batchSnapshot = {
8143
+ query: this.state.query,
8144
+ past: [...this.state.past],
8145
+ future: [...this.state.future],
8146
+ lastSig: this.state.lastSig,
8147
+ lastAt: this.state.lastAt
7311
8148
  };
7312
8149
  try {
7313
8150
  fn();
7314
8151
  } catch (error) {
7315
- if (this.#batchDepth === 1) {
7316
- const snapshot = this.#batchSnapshot;
7317
- this.#query = snapshot.query;
7318
- this.#past = snapshot.past;
7319
- this.#future = snapshot.future;
7320
- this.#lastSig = snapshot.lastSig;
7321
- this.#lastAt = snapshot.lastAt;
7322
- this.#reconcileHistoryConfig();
8152
+ if (this.state.batchDepth === 1) {
8153
+ const snapshot = this.state.batchSnapshot;
8154
+ this.state.query = snapshot.query;
8155
+ this.state.past = snapshot.past;
8156
+ this.state.future = snapshot.future;
8157
+ this.state.lastSig = snapshot.lastSig;
8158
+ this.state.lastAt = snapshot.lastAt;
8159
+ this.reconcileHistoryConfig();
7323
8160
  }
7324
8161
  throw error;
7325
8162
  } finally {
7326
- this.#batchDepth--;
7327
- if (this.#batchDepth === 0) {
7328
- const { query: base } = this.#batchSnapshot;
7329
- this.#batchSnapshot = void 0;
7330
- if (base !== this.#query) {
7331
- if (!this.#historyBypassed) this.#record(base, this.#query);
7332
- this.#notify();
8163
+ this.state.batchDepth--;
8164
+ if (this.state.batchDepth === 0) {
8165
+ const { query: base } = this.state.batchSnapshot;
8166
+ this.state.batchSnapshot = void 0;
8167
+ const queryChanged = base !== this.state.query;
8168
+ const configChanged = this.state.pendingConfigNotify;
8169
+ this.state.pendingConfigNotify = false;
8170
+ if (queryChanged) {
8171
+ if (!this.state.historyBypassed) this.record(base, this.state.query);
7333
8172
  }
7334
- this.#historyBypassed = false;
8173
+ if (queryChanged || configChanged) this.notify(queryChanged ? configChanged ? queryAndConfigChange : queryChange : configChange);
8174
+ this.state.historyBypassed = false;
7335
8175
  }
7336
8176
  }
7337
8177
  return this;
7338
8178
  }
7339
8179
  /** Whether there is a previous query to restore. Always `false` unless `history` is enabled. */
7340
8180
  canUndo() {
7341
- return this.#past.length > 0;
8181
+ return this.state.past.length > 0;
7342
8182
  }
7343
8183
  /** Whether there is an undone query to restore. Always `false` unless `history` is enabled. */
7344
8184
  canRedo() {
7345
- return this.#future.length > 0;
8185
+ return this.state.future.length > 0;
7346
8186
  }
7347
8187
  /** Restores the previous query. No-op when {@link QueryManager.canUndo} is `false`. */
7348
8188
  undo() {
7349
- if (this.#past.length === 0) return this;
7350
- this.#future.unshift(this.#query);
7351
- this.#query = this.#past.pop();
7352
- this.#lastSig = void 0;
7353
- this.#markHistoryBypassed();
7354
- if (this.#batchDepth === 0) this.#notify();
8189
+ if (this.state.past.length === 0) return this;
8190
+ this.state.future.unshift(this.state.query);
8191
+ this.state.query = this.state.past.pop();
8192
+ this.state.lastSig = void 0;
8193
+ this.markHistoryBypassed();
8194
+ if (this.state.batchDepth === 0) this.notify(queryChange);
7355
8195
  return this;
7356
8196
  }
7357
8197
  /** Restores the most recently undone query. No-op when {@link QueryManager.canRedo} is `false`. */
7358
8198
  redo() {
7359
- if (this.#future.length === 0) return this;
7360
- this.#past.push(this.#query);
7361
- this.#query = this.#future.shift();
7362
- this.#lastSig = void 0;
7363
- this.#markHistoryBypassed();
7364
- if (this.#batchDepth === 0) this.#notify();
8199
+ if (this.state.future.length === 0) return this;
8200
+ this.state.past.push(this.state.query);
8201
+ this.state.query = this.state.future.shift();
8202
+ this.state.lastSig = void 0;
8203
+ this.markHistoryBypassed();
8204
+ if (this.state.batchDepth === 0) this.notify(queryChange);
7365
8205
  return this;
7366
8206
  }
7367
8207
  /** Discards all undo/redo history without changing the current query. */
7368
8208
  clearHistory() {
7369
- this.#markHistoryBypassed();
7370
- this.#past = [];
7371
- this.#future = [];
7372
- this.#lastSig = void 0;
8209
+ this.markHistoryBypassed();
8210
+ this.state.past = [];
8211
+ this.state.future = [];
8212
+ this.state.lastSig = void 0;
7373
8213
  return this;
7374
8214
  }
7375
8215
  /**
@@ -7378,8 +8218,8 @@ var QueryManager = class QueryManager {
7378
8218
  */
7379
8219
  getHistory() {
7380
8220
  return {
7381
- past: [...this.#past],
7382
- future: [...this.#future]
8221
+ past: [...this.state.past],
8222
+ future: [...this.state.future]
7383
8223
  };
7384
8224
  }
7385
8225
  /**
@@ -7389,12 +8229,12 @@ var QueryManager = class QueryManager {
7389
8229
  * one that depends on anything other than the query) may run fewer times than expected.
7390
8230
  */
7391
8231
  validate() {
7392
- this.#ensureCache();
7393
- this.#validation ??= this.#validator(this.#query);
7394
- return this.#validation;
8232
+ this.ensureCache();
8233
+ this.state.validation ??= this.state.validator(this.state.query);
8234
+ return this.state.validation;
7395
8235
  }
7396
8236
  format(options) {
7397
- return formatQuery(this.#query, options);
8237
+ return formatQuery(this.state.query, options);
7398
8238
  }
7399
8239
  /**
7400
8240
  * Yields every rule and group in the query, depth-first in pre-order, starting with the root
@@ -7414,18 +8254,18 @@ var QueryManager = class QueryManager {
7414
8254
  */
7415
8255
  *walk(options = {}) {
7416
8256
  const { from, rulesOnly, groupsOnly } = options;
7417
- let start = this.#query;
8257
+ let start = this.state.query;
7418
8258
  let startPath = [];
7419
8259
  let startParent = null;
7420
8260
  if (from !== void 0) {
7421
- const path = this.#toPath(from);
7422
- const node = path && findPath(path, this.#query);
8261
+ const path = this.toPath(from);
8262
+ const node = path && findPath(path, this.state.query);
7423
8263
  if (!path || !node) return;
7424
8264
  start = node;
7425
8265
  startPath = path;
7426
- startParent = path.length === 0 ? null : findPath(getParentPath(path), this.#query);
8266
+ startParent = path.length === 0 ? null : findPath(getParentPath(path), this.state.query);
7427
8267
  }
7428
- for (const entry of this.#walkFrom(start, startPath, startParent)) {
8268
+ for (const entry of this.walkFrom(start, startPath, startParent)) {
7429
8269
  if (rulesOnly && isRuleGroup(entry.node)) continue;
7430
8270
  if (groupsOnly && !isRuleGroup(entry.node)) continue;
7431
8271
  yield entry;
@@ -7470,34 +8310,34 @@ var QueryManager = class QueryManager {
7470
8310
  * index, unresolvable paths are always normalized to `null` here.
7471
8311
  */
7472
8312
  findPath(path) {
7473
- return findPath(path, this.#query) ?? null;
8313
+ return findPath(path, this.state.query) ?? null;
7474
8314
  }
7475
8315
  /**
7476
8316
  * Returns the rule or group with the given `id`, or `null` if there is none. Backed by an
7477
8317
  * index built once per query, so repeated lookups are constant time.
7478
8318
  */
7479
8319
  findID(id) {
7480
- const path = this.#index().get(id);
7481
- return path === void 0 ? null : findPath(path, this.#query);
8320
+ const path = this.index().get(id);
8321
+ return path === void 0 ? null : findPath(path, this.state.query);
7482
8322
  }
7483
8323
  /**
7484
8324
  * Returns the {@link Path} of the rule or group with the given `id`, or `null` if there is
7485
8325
  * none. Backed by an index built once per query, so repeated lookups are constant time.
7486
8326
  */
7487
8327
  getPathOfID(id) {
7488
- return this.#index().get(id) ?? null;
8328
+ return this.index().get(id) ?? null;
7489
8329
  }
7490
8330
  /**
7491
8331
  * Determines whether the rule or group at the given path is disabled, either itself or by an
7492
8332
  * ancestor group.
7493
8333
  */
7494
8334
  pathIsDisabled(path) {
7495
- return pathIsDisabled(path, this.#query);
8335
+ return pathIsDisabled(path, this.state.query);
7496
8336
  }
7497
8337
  /** Returns the rule or group at the given path or `id`, or `null` if it can't be resolved. */
7498
8338
  getNode(pathOrID) {
7499
- const path = this.#toPath(pathOrID);
7500
- return path === null ? null : findPath(path, this.#query) ?? null;
8339
+ const path = this.toPath(pathOrID);
8340
+ return path === null ? null : findPath(path, this.state.query) ?? null;
7501
8341
  }
7502
8342
  /**
7503
8343
  * Returns the rule at the given path or `id`, or `null` if it can't be resolved _or_ resolves
@@ -7520,24 +8360,24 @@ var QueryManager = class QueryManager {
7520
8360
  * for the root group, which has no parent, and when the target can't be resolved.
7521
8361
  */
7522
8362
  getParent(pathOrID) {
7523
- const path = this.#toPath(pathOrID);
8363
+ const path = this.toPath(pathOrID);
7524
8364
  if (path === null || path.length === 0) return null;
7525
- if (!findPath(path, this.#query)) return null;
7526
- return findPath(getParentPath(path), this.#query);
8365
+ if (!findPath(path, this.state.query)) return null;
8366
+ return findPath(getParentPath(path), this.state.query);
7527
8367
  }
7528
8368
  /**
7529
8369
  * The normalized field list, as the `QueryBuilder` component would render it. Needed to
7530
8370
  * populate a field selector.
7531
8371
  */
7532
8372
  getFields() {
7533
- return this.#fields;
8373
+ return this.state.fields;
7534
8374
  }
7535
8375
  /**
7536
8376
  * The normalized combinator list, as the `QueryBuilder` component would render it. Needed to
7537
8377
  * populate a combinator selector.
7538
8378
  */
7539
8379
  getCombinators() {
7540
- return this.#combinators;
8380
+ return this.state.combinators;
7541
8381
  }
7542
8382
  /**
7543
8383
  * The field configuration for a field name. When the field isn't configured, returns the same
@@ -7545,27 +8385,51 @@ var QueryManager = class QueryManager {
7545
8385
  * {@link QueryManager.getRuleContext} reports as `fieldData`, so both access paths agree.
7546
8386
  */
7547
8387
  getFieldData(field) {
7548
- return getFieldData(field, this.#fieldMap);
8388
+ return getFieldData(field, this.state.fieldMap);
7549
8389
  }
7550
8390
  /** The operator list for a field, mirroring `QueryBuilder`'s precedence. */
7551
8391
  getOperators(field) {
7552
- return this.#operatorsFor(field);
8392
+ return this.operatorsFor(field);
7553
8393
  }
7554
8394
  /** The value sources available for a field/operator pair. */
7555
8395
  getValueSources(field, operator) {
7556
- return this.#valueSourcesFor(field, operator);
8396
+ return this.valueSourcesFor(field, operator);
7557
8397
  }
7558
8398
  /** The match modes available for a field. */
7559
8399
  getMatchModes(field) {
7560
- return this.#matchModesFor(field);
8400
+ return this.matchModesFor(field);
7561
8401
  }
7562
8402
  /** The value option list for a field/operator pair. */
7563
8403
  getValues(field, operator) {
7564
- return this.#valuesFor(field, operator);
8404
+ return this.valuesFor(field, operator);
7565
8405
  }
7566
8406
  /** The value editor type for a field/operator pair. */
7567
8407
  getValueEditorType(field, operator) {
7568
- return this.#valueEditorTypeFor(field, operator);
8408
+ return this.valueEditorTypeFor(field, operator);
8409
+ }
8410
+ /**
8411
+ * The default `operator` for a field, identical to the operator
8412
+ * {@link QueryManager.createRule} would assign to a new rule on that field.
8413
+ */
8414
+ getRuleDefaultOperator(field) {
8415
+ return this.defaultOperator(field);
8416
+ }
8417
+ /**
8418
+ * The default `value` for a rule, identical to the value {@link QueryManager.createRule} and
8419
+ * {@link QueryManager.update} would assign after a field or operator change.
8420
+ */
8421
+ getRuleDefaultValue(rule) {
8422
+ return this.defaultValue(rule);
8423
+ }
8424
+ /**
8425
+ * The flattened field record backing {@link QueryManager.getFieldData}, keyed by field name
8426
+ * with option groups flattened away.
8427
+ *
8428
+ * Treat the result as read-only. It may or may not be frozen depending on configuration, so
8429
+ * do not rely on frozen-ness to prevent mutation.
8430
+ */
8431
+ getFieldMap() {
8432
+ return this.state.fieldMap;
7569
8433
  }
7570
8434
  /**
7571
8435
  * Resolves everything about a rule that depends on the field/operator configuration—field data,
@@ -7580,16 +8444,16 @@ var QueryManager = class QueryManager {
7580
8444
  if (!rule) return null;
7581
8445
  const validation = this.validate();
7582
8446
  return deriveRuleContext(rule, {
7583
- fields: this.#fields,
7584
- fieldMap: this.#fieldMap,
7585
- getInputType: (f, o, misc) => this.#options.getInputType?.(f, o, misc) ?? null,
7586
- getMatchModes: (f) => this.#matchModesFor(f),
7587
- getOperators: (f) => this.#operatorsFor(f),
7588
- getParameters: (f, o, misc) => this.#parametersFor(f, o, misc),
7589
- getValueEditorType: (f, o) => this.#valueEditorTypeFor(f, o),
7590
- getValues: (f, o) => this.#valuesFor(f, o),
8447
+ fields: this.state.fields,
8448
+ fieldMap: this.state.fieldMap,
8449
+ getInputType: (f, o, misc) => this.state.options.getInputType?.(f, o, misc) ?? null,
8450
+ getMatchModes: (f) => this.matchModesFor(f),
8451
+ getOperators: (f) => this.operatorsFor(f),
8452
+ getParameters: (f, o, misc) => this.parametersFor(f, o, misc),
8453
+ getValueEditorType: (f, o) => this.valueEditorTypeFor(f, o),
8454
+ getValues: (f, o) => this.valuesFor(f, o),
7591
8455
  getValueSources: (f, o) => this.getValueSources(f, o),
7592
- getSubQueryBuilderProps: (f, misc) => this.#options.getSubQueryBuilderProps?.(f, misc) ?? {}
8456
+ getSubQueryBuilderProps: (f, misc) => this.state.options.getSubQueryBuilderProps?.(f, misc) ?? {}
7593
8457
  }, {
7594
8458
  validationMap: typeof validation === "boolean" ? {} : validation,
7595
8459
  id: rule.id
@@ -7605,32 +8469,32 @@ var QueryManager = class QueryManager {
7605
8469
  const ruleGroup = this.getGroup(pathOrID);
7606
8470
  if (!ruleGroup) return null;
7607
8471
  const validation = this.validate();
7608
- return deriveRuleGroupContext(ruleGroup, this.#combinators, {
8472
+ return deriveRuleGroupContext(ruleGroup, this.state.combinators, {
7609
8473
  validationMap: typeof validation === "boolean" ? {} : validation,
7610
8474
  id: ruleGroup.id
7611
8475
  });
7612
8476
  }
7613
8477
  /** Whether the current query uses independent combinators. */
7614
8478
  isIC() {
7615
- return isRuleGroupTypeIC(this.#query);
8479
+ return isRuleGroupTypeIC(this.state.query);
7616
8480
  }
7617
8481
  /**
7618
8482
  * Returns the signature describing how the current query differs from `other`, as used by
7619
8483
  * this manager's history coalescing.
7620
8484
  */
7621
8485
  signatureOf(other) {
7622
- return signatureOf(this.#query, other);
8486
+ return signatureOf(this.state.query, other);
7623
8487
  }
7624
8488
  /** Generates a {@link DiagnosticsResult}. Shorthand for `format('diagnostics')`. */
7625
8489
  diagnostics() {
7626
- return formatQuery(this.#query, "diagnostics");
8490
+ return formatQuery(this.state.query, "diagnostics");
7627
8491
  }
7628
8492
  /**
7629
8493
  * Returns the current query, so `JSON.stringify(queryManager)` produces the same output as
7630
8494
  * `JSON.stringify(queryManager.getQuery())`.
7631
8495
  */
7632
8496
  toJSON() {
7633
- return this.#query;
8497
+ return this.state.query;
7634
8498
  }
7635
8499
  /**
7636
8500
  * Returns a new manager with the same configuration and the current query converted to use
@@ -7638,7 +8502,7 @@ var QueryManager = class QueryManager {
7638
8502
  * {@link QueryManager.clone}, subscribers and history are not carried over.
7639
8503
  */
7640
8504
  toIC() {
7641
- return new QueryManager(convertToIC(this.#query), this.#options);
8505
+ return new QueryManager(convertToIC(this.state.query), this.state.options);
7642
8506
  }
7643
8507
  /**
7644
8508
  * Returns a new manager with the same configuration and the current query converted to use a
@@ -7646,7 +8510,7 @@ var QueryManager = class QueryManager {
7646
8510
  * {@link QueryManager.clone}, subscribers and history are not carried over.
7647
8511
  */
7648
8512
  fromIC() {
7649
- return new QueryManager(convertFromIC(this.#query), this.#options);
8513
+ return new QueryManager(convertFromIC(this.state.query), this.state.options);
7650
8514
  }
7651
8515
  /**
7652
8516
  * Runs {@link transformQuery} against the current query and returns its result.
@@ -7656,7 +8520,7 @@ var QueryManager = class QueryManager {
7656
8520
  * shapes that are no longer valid queries. This manager is never modified.
7657
8521
  */
7658
8522
  transform(options) {
7659
- return transformQuery(this.#query, options);
8523
+ return transformQuery(this.state.query, options);
7660
8524
  }
7661
8525
  };
7662
8526
  //#endregion
@@ -7673,6 +8537,9 @@ exports.celCombinatorMap = celCombinatorMap;
7673
8537
  exports.clsx = clsx;
7674
8538
  exports.coerceBigIntValue = coerceBigIntValue;
7675
8539
  exports.coerceInputType = coerceInputType;
8540
+ exports.controlKeys = controlKeys;
8541
+ exports.controlKind = controlKind;
8542
+ exports.controlPropKeys = controlPropKeys;
7676
8543
  exports.convertFromIC = convertFromIC;
7677
8544
  exports.convertQuery = convertQuery;
7678
8545
  exports.convertToIC = convertToIC;
@@ -7848,6 +8715,7 @@ exports.numericRegex = numericRegex;
7848
8715
  exports.numerifyValues = numerifyValues;
7849
8716
  exports.objectEntries = objectEntries;
7850
8717
  exports.objectKeys = objectKeys;
8718
+ exports.optionsEqual = optionsEqual;
7851
8719
  exports.parseNumber = parseNumber;
7852
8720
  exports.pathIsDisabled = pathIsDisabled;
7853
8721
  exports.pathIsDisabledByPaths = pathIsDisabledByPaths;
@@ -7874,6 +8742,8 @@ exports.resolveOperatorList = resolveOperatorList;
7874
8742
  exports.resolveValueEditorType = resolveValueEditorType;
7875
8743
  exports.resolveValueList = resolveValueList;
7876
8744
  exports.rootPath = rootPath;
8745
+ exports.setAutoFreeze = setAutoFreeze;
8746
+ exports.shouldCoalesce = shouldCoalesce;
7877
8747
  exports.shouldRenderAsNumber = shouldRenderAsNumber;
7878
8748
  exports.signatureOf = signatureOf;
7879
8749
  exports.sparqlVar = sparqlVar;
@@ -7900,6 +8770,7 @@ exports.uniqOptList = uniqOptList;
7900
8770
  exports.update = update;
7901
8771
  exports.updateInPlace = updateInPlace;
7902
8772
  exports.uuidV4regex = uuidV4regex;
8773
+ exports.valuesEqual = valuesEqual;
7903
8774
  exports.withParamPrefix = withParamPrefix;
7904
8775
  exports.wrapLikeFragment = wrapLikeFragment;
7905
8776