@object-ui/react 17.6.0 → 17.7.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 (89) hide show
  1. package/CHANGELOG.md +3766 -0
  2. package/README.md +294 -38
  3. package/dist/SchemaRenderer.d.ts +38 -7
  4. package/dist/SchemaRenderer.js +1218 -87
  5. package/dist/context/ActionContext.js +40 -1
  6. package/dist/context/DiscussionContext.d.ts +12 -0
  7. package/dist/context/DiscussionContext.js +2 -0
  8. package/dist/context/DrillNavigationContext.d.ts +13 -0
  9. package/dist/context/InlineEditContext.d.ts +81 -1
  10. package/dist/context/InlineEditContext.js +8 -2
  11. package/dist/context/RecordContext.d.ts +48 -5
  12. package/dist/context/RecordContext.js +10 -1
  13. package/dist/context/RelatedRecordActionsContext.d.ts +5 -1
  14. package/dist/context/SchemaRendererContext.d.ts +54 -2
  15. package/dist/context/SchemaRendererContext.js +38 -5
  16. package/dist/context/ThemeContext.js +1 -1
  17. package/dist/context/actionRunnerTranslator.d.ts +27 -0
  18. package/dist/context/actionRunnerTranslator.js +37 -0
  19. package/dist/element-data-source/ElementDataSourceGate.d.ts +88 -1
  20. package/dist/element-data-source/ElementDataSourceGate.js +326 -13
  21. package/dist/hooks/index.d.ts +3 -1
  22. package/dist/hooks/index.js +10 -1
  23. package/dist/hooks/useActionEngine.js +46 -3
  24. package/dist/hooks/useActionRunner.js +4 -0
  25. package/dist/hooks/useActionTextLocalizer.d.ts +21 -8
  26. package/dist/hooks/useActionTextLocalizer.js +33 -8
  27. package/dist/hooks/useConfigBagEvaluator.d.ts +30 -0
  28. package/dist/hooks/useConfigBagEvaluator.js +39 -0
  29. package/dist/hooks/useDensityMode.d.ts +6 -2
  30. package/dist/hooks/useDensityMode.js +6 -2
  31. package/dist/hooks/useDiscovery.d.ts +1 -10
  32. package/dist/hooks/useETagCache.js +25 -4
  33. package/dist/hooks/useElementDataSource.d.ts +15 -2
  34. package/dist/hooks/useElementDataSource.js +24 -7
  35. package/dist/hooks/useExpression.d.ts +52 -59
  36. package/dist/hooks/useExpression.js +58 -90
  37. package/dist/hooks/useFilterScope.d.ts +22 -2
  38. package/dist/hooks/useFilterScope.js +29 -4
  39. package/dist/hooks/useGlobalUndo.d.ts +10 -0
  40. package/dist/hooks/useGlobalUndo.js +112 -46
  41. package/dist/hooks/useNavigationOverlay.d.ts +76 -11
  42. package/dist/hooks/useNavigationOverlay.js +119 -9
  43. package/dist/hooks/useOffline.js +65 -11
  44. package/dist/hooks/usePageAssignment.d.ts +8 -5
  45. package/dist/hooks/usePageAssignment.js +37 -16
  46. package/dist/hooks/useRecordSearch.d.ts +34 -1
  47. package/dist/hooks/useRecordSearch.js +42 -7
  48. package/dist/hooks/useResolvedFilter.d.ts +46 -0
  49. package/dist/hooks/useResolvedFilter.js +130 -0
  50. package/dist/hooks/useSchemaPersistence.d.ts +7 -0
  51. package/dist/hooks/useSchemaPersistence.js +144 -5
  52. package/dist/hooks/useSettledSchema.d.ts +123 -0
  53. package/dist/hooks/useSettledSchema.js +186 -0
  54. package/dist/hooks/useViewData.d.ts +7 -2
  55. package/dist/index.d.ts +8 -1
  56. package/dist/index.js +45 -1
  57. package/dist/schema-input.d.ts +38 -8
  58. package/dist/schema-input.js +39 -7
  59. package/dist/utils/configBag.d.ts +68 -0
  60. package/dist/utils/configBag.js +70 -0
  61. package/dist/utils/error-message.d.ts +1 -1
  62. package/dist/utils/error-message.js +1 -1
  63. package/dist/utils/inlineAria.d.ts +61 -0
  64. package/dist/utils/inlineAria.js +70 -0
  65. package/dist/utils/nonGridRowCeiling.d.ts +44 -0
  66. package/dist/utils/nonGridRowCeiling.js +78 -0
  67. package/dist/utils/paramsBag.d.ts +28 -0
  68. package/dist/utils/paramsBag.js +146 -0
  69. package/dist/utils/propsBagDiagnostic.d.ts +196 -0
  70. package/dist/utils/propsBagDiagnostic.js +421 -0
  71. package/dist/utils/refusedDataPropDiagnostic.d.ts +44 -0
  72. package/dist/utils/refusedDataPropDiagnostic.js +94 -0
  73. package/dist/utils/unevaluatedExpression.d.ts +42 -1
  74. package/dist/utils/unevaluatedExpression.js +74 -11
  75. package/dist/utils/visibilityDiagnostic.d.ts +271 -19
  76. package/dist/utils/visibilityDiagnostic.js +311 -44
  77. package/package.json +10 -10
  78. package/dist/hooks/useClientNotifications.d.ts +0 -47
  79. package/dist/hooks/useClientNotifications.js +0 -162
  80. package/dist/spec-bridge/SpecBridge.d.ts +0 -28
  81. package/dist/spec-bridge/SpecBridge.js +0 -56
  82. package/dist/spec-bridge/bridges/form-view.d.ts +0 -158
  83. package/dist/spec-bridge/bridges/form-view.js +0 -170
  84. package/dist/spec-bridge/bridges/list-view.d.ts +0 -20
  85. package/dist/spec-bridge/bridges/list-view.js +0 -268
  86. package/dist/spec-bridge/index.d.ts +0 -11
  87. package/dist/spec-bridge/index.js +0 -10
  88. package/dist/spec-bridge/types.d.ts +0 -55
  89. package/dist/spec-bridge/types.js +0 -8
@@ -0,0 +1,28 @@
1
+ /**
2
+ * ObjectUI
3
+ * Copyright (c) 2024-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ /** The key this rule is about. One spelling, shared by both readers. */
9
+ export declare const PARAMS_KEY = "params";
10
+ /**
11
+ * Is this value a `params` BAG this rule walks? A plain object only - see the
12
+ * module header for why an array `params` (the definition list) is not.
13
+ */
14
+ export declare function isParamsBag(value: unknown): value is Record<string, unknown>;
15
+ /**
16
+ * Visit one string leaf. `path` spells where it sits the way an author would
17
+ * search for it: `params.target.id`, `params.ids[0]`.
18
+ */
19
+ export type ParamsLeafVisitor = (leaf: string, path: string) => unknown;
20
+ /**
21
+ * Map every string leaf of a `params` bag through `visit`, at any depth, and
22
+ * return the result - the bag itself when nothing changed (copy on write).
23
+ *
24
+ * The evaluation memo passes the evaluator; the dev diagnostic passes a visitor
25
+ * that records and returns the leaf unchanged, which is what makes the two
26
+ * radii one radius by construction.
27
+ */
28
+ export declare function mapParamsLeaves(bag: Record<string, unknown>, visit: ParamsLeafVisitor, basePath?: string): Record<string, unknown>;
@@ -0,0 +1,146 @@
1
+ /**
2
+ * ObjectUI
3
+ * Copyright (c) 2024-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ /**
9
+ * An action's `params` values are templates, evaluated where `properties` are
10
+ * (objectui#7867, ruling A, maintainer 「其他同意」 2026-09-20).
11
+ *
12
+ * ## The rule
13
+ *
14
+ * Every STRING LEAF of a node's `params` bag - the node-level `params` and
15
+ * `properties.params` alike, at any depth - is template-evaluated by the
16
+ * `SchemaRenderer` evaluation memo, with the same evaluator and the same
17
+ * scope (`record`, `current_user`, `page`, the host's ambient roots) that
18
+ * already evaluates `properties`. Before it, `params` was the one argument bag
19
+ * an `action:button` hands the runner, and nothing walked it: the node-level
20
+ * bag was never evaluated at all, and `properties.params` was ONE value of a
21
+ * per-value, shallow loop, so a template one level inside it was never
22
+ * visited. A metadata-authored `navigate_edit` could not name the record it
23
+ * sits on.
24
+ *
25
+ * ## Why this module exists, and why it is not in `SchemaRenderer.tsx`
26
+ *
27
+ * Two readers must agree on exactly one radius: the memo that EVALUATES the
28
+ * leaves, and the dev diagnostic (`unevaluatedExpression.ts`) that REPORTS the
29
+ * leaves still carrying `${…}` afterwards. That diagnostic's header states its
30
+ * own rule - "Depth: exactly the evaluator's own radius, and not one level
31
+ * more" - so a second copy of this walk would be a second definition of that
32
+ * radius, free to drift. Both import this one. It lives in its own module
33
+ * rather than in the diagnostic's because the memo runs in production and the
34
+ * diagnostic module is meant to be dead code there.
35
+ *
36
+ * ## What counts as a bag, a container and a leaf
37
+ *
38
+ * - **The bag** is a PLAIN object: an object literal or JSON object, whose
39
+ * prototype is `Object.prototype` or `null`. An ARRAY `params` is not a bag:
40
+ * it is the `ActionParam[]` definition list `action:button` routes to
41
+ * `actionParams` for the params dialog, whose members carry predicates and
42
+ * formats (`visible`, `titleFormat`, option `visibleWhen`) that are evaluated
43
+ * later, against another scope. It is left untouched, as before.
44
+ * - **Containers** inside the bag - plain objects and arrays - are walked.
45
+ * - **String leaves** are handed to the visitor. Everything else is passed
46
+ * through by IDENTITY and never looked inside: numbers, booleans, `null`,
47
+ * `undefined`, functions, and non-plain objects (`Date`, `Map`, class
48
+ * instances). Those are host-built values, not authored templates.
49
+ * - **Keys are never visited**, only values. A key that looks like a template
50
+ * stays exactly as written.
51
+ * - **An expression envelope** (`{ dialect, source }`) is a plain object like
52
+ * any other: it is walked, and its `source` string is a leaf. It is never
53
+ * collapsed to its `source` the way a bare top-level config value is.
54
+ * - **Cycles** end the walk: a container already on the current path is passed
55
+ * through unwalked. JSON metadata cannot carry one; a host-built schema can.
56
+ *
57
+ * ## Copy on write
58
+ *
59
+ * A container is copied only when one of its descendants changed, so a bag
60
+ * with no template in it comes back as the very object that went in, and a
61
+ * changed bag never mutates the authored one (the memo's input is the
62
+ * caller's schema). Nothing may depend on that identity (AGENTS.md #10); it is
63
+ * there so that a template-free bag stays byte-for-byte and reference-for-
64
+ * reference what it was.
65
+ */
66
+ import { isConfigBag } from './configBag.js';
67
+ /** The key this rule is about. One spelling, shared by both readers. */
68
+ export const PARAMS_KEY = 'params';
69
+ /**
70
+ * A plain object: an object literal / JSON object, and nothing more exotic.
71
+ *
72
+ * Built ON the package's one "is this a config bag?" answer (objectui#6761)
73
+ * and narrowed by prototype, rather than re-spelling the object/array test:
74
+ * this rule asks that question plus one more (a `Date` or class instance is a
75
+ * bag to {@link isConfigBag}, and is not something this walk looks inside).
76
+ */
77
+ function isPlainObject(value) {
78
+ if (!isConfigBag(value))
79
+ return false;
80
+ const proto = Object.getPrototypeOf(value);
81
+ return proto === Object.prototype || proto === null;
82
+ }
83
+ /**
84
+ * Is this value a `params` BAG this rule walks? A plain object only - see the
85
+ * module header for why an array `params` (the definition list) is not.
86
+ */
87
+ export function isParamsBag(value) {
88
+ return isPlainObject(value);
89
+ }
90
+ function walk(value, path, visit, onPath) {
91
+ if (typeof value === 'string')
92
+ return visit(value, path);
93
+ if (Array.isArray(value)) {
94
+ if (onPath.has(value))
95
+ return value;
96
+ onPath.add(value);
97
+ let copy;
98
+ for (let i = 0; i < value.length; i++) {
99
+ const next = walk(value[i], path + '[' + i + ']', visit, onPath);
100
+ if (next !== value[i]) {
101
+ if (!copy)
102
+ copy = value.slice();
103
+ copy[i] = next;
104
+ }
105
+ }
106
+ onPath.delete(value);
107
+ return copy ?? value;
108
+ }
109
+ if (isPlainObject(value)) {
110
+ if (onPath.has(value))
111
+ return value;
112
+ onPath.add(value);
113
+ let copy;
114
+ for (const key of Object.keys(value)) {
115
+ const leaf = value[key];
116
+ const next = walk(leaf, path + '.' + key, visit, onPath);
117
+ if (next !== leaf) {
118
+ if (!copy)
119
+ copy = { ...value };
120
+ // `defineProperty`, not assignment: an own `__proto__` key (which
121
+ // `JSON.parse` does produce) must stay an own data property rather than
122
+ // reach the prototype setter.
123
+ Object.defineProperty(copy, key, {
124
+ value: next,
125
+ writable: true,
126
+ enumerable: true,
127
+ configurable: true,
128
+ });
129
+ }
130
+ }
131
+ onPath.delete(value);
132
+ return copy ?? value;
133
+ }
134
+ return value;
135
+ }
136
+ /**
137
+ * Map every string leaf of a `params` bag through `visit`, at any depth, and
138
+ * return the result - the bag itself when nothing changed (copy on write).
139
+ *
140
+ * The evaluation memo passes the evaluator; the dev diagnostic passes a visitor
141
+ * that records and returns the leaf unchanged, which is what makes the two
142
+ * radii one radius by construction.
143
+ */
144
+ export function mapParamsLeaves(bag, visit, basePath = PARAMS_KEY) {
145
+ return walk(bag, basePath, visit, new Set());
146
+ }
@@ -0,0 +1,196 @@
1
+ /**
2
+ * ObjectUI
3
+ * Copyright (c) 2024-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ /**
9
+ * Does this node's renderer read the `props` bag as a config bag?
10
+ *
11
+ * Exported so the report and its pins cannot drift apart: one predicate, two
12
+ * readers. A non-string `type` answers `false` — an untyped node cannot be in
13
+ * a family — and the caller has already resolved a component for it, so the
14
+ * unknown-type box is not in play.
15
+ */
16
+ export declare function readsPropsBag(type: unknown): boolean;
17
+ /** Prefix every line starts with — the handle tests, greps and log filters hold. */
18
+ export declare const DROPPED_PROPS_BAG_PREFIX = "[ObjectUI] A `props` config bag was not read";
19
+ /**
20
+ * Build the message. Separate from the emit so a test can assert the words a
21
+ * developer is going to read, not merely that something was logged.
22
+ */
23
+ export declare function formatDroppedPropsBagMessage(type: unknown, id: unknown, droppedKeys: readonly string[]): string;
24
+ /**
25
+ * Test-only reset for the dedupe above. Without it the second test to assert
26
+ * the same warning reads the first test's dedupe entry and sees silence — a
27
+ * green run that checked nothing.
28
+ */
29
+ export declare function __resetDroppedPropsBagWarnings(): void;
30
+ /**
31
+ * Which keys of the OUTGOING props bag are dropped by a `schema`-reading
32
+ * renderer, or `null` when there is nothing to say.
33
+ *
34
+ * Two bags are read, and needing both is a MEASUREMENT rather than caution.
35
+ *
36
+ * `outgoingPropsBag` is the bag `SchemaRenderer` actually spreads — the value
37
+ * of `propsWithoutCanonicalKeys(schema.props, schema.properties)`, passed in
38
+ * rather than re-derived. That is what makes the message honest about the two
39
+ * cases it must not confuse:
40
+ *
41
+ * - a key BOTH bags declare has already been subtracted by that function
42
+ * (objectui#5123: `properties` wins on both channels), so the author is
43
+ * getting the canonical answer and nothing was silently dropped;
44
+ * - a key only `props` declares survives into that bag, and it is exactly the
45
+ * key that reaches no `schema` reader.
46
+ *
47
+ * `authoredPropsBag` is what the AUTHOR wrote, read off the original schema
48
+ * before the evaluation memo touched it. It is needed because that memo
49
+ * rebuilds the bag with `{ ...newSchema.props }` under a bare truthiness guard,
50
+ * so a degenerate `props: 'not-a-bag'` arrives at the spread site as
51
+ * `{ '0': 'n', '1': 'o', … }` — measured, not supposed. Reading only the
52
+ * outgoing bag would therefore report nine dropped keys named `0` … `8` and
53
+ * tell the author that `schema.0` is undefined, which is true and useless. A
54
+ * string is not a config bag; there is nothing to point at, and the shape-level
55
+ * defect it represents is a different question from this one.
56
+ *
57
+ * Deliberately narrow, and each exclusion is a reading rather than an omission:
58
+ *
59
+ * - an EMPTY bag says nothing was authored and nothing was lost;
60
+ * - a non-object authored `props` is the degenerate case above;
61
+ * - a node in the {@link readsPropsBag} family is silent by the ruling.
62
+ */
63
+ export declare function collectDroppedPropsKeys(type: unknown, authoredPropsBag: unknown, outgoingPropsBag: unknown): string[] | null;
64
+ /**
65
+ * Dev-build only. Reports once per distinct message via `console.warn`, and
66
+ * returns the message it emitted (or `null`) so a caller or a test can read the
67
+ * decision rather than infer it from a spy.
68
+ *
69
+ * The caller applies the production gate, so this stays a single dev-only
70
+ * branch at the call site and the whole module is dead code in a production
71
+ * build.
72
+ */
73
+ export declare function reportDroppedPropsBag(type: unknown, id: unknown, authoredPropsBag: unknown, outgoingPropsBag: unknown): string | null;
74
+ /**
75
+ * Refusal: a NODE-GATE PREDICATE was parked under `props` (objectui#9108,
76
+ * maintainer ruling 2026-09-13, verbatim 「同意」 on the `domain:spec` seat's
77
+ * recommendation - REFUSE).
78
+ *
79
+ * ## The defect this refuses
80
+ *
81
+ * Measured at node level on `1e0e46af9`, four rows, both spellings and both
82
+ * polarities:
83
+ *
84
+ * { type: 'card', props: { visible: false } } -> the node RENDERED
85
+ * { type: 'card', props: { hidden: true } } -> the node RENDERED
86
+ * { type: 'card', properties: { visible: false } } -> correctly hidden
87
+ * { type: 'card', properties: { hidden: true } } -> correctly hidden
88
+ *
89
+ * `SchemaRenderer` hoists `properties.*` onto the node; NOTHING copies
90
+ * `props.*`. Both node gates read the post-hoist node, so a predicate that
91
+ * arrived under the alias is never one of the keys either gate can see. It is
92
+ * fail-OPEN and silent by construction - a gate that never bit renders exactly
93
+ * like a gate that said yes - so no user, no screenshot and no snapshot can
94
+ * find it. Only counting can.
95
+ *
96
+ * ## Why REFUSE and not HONOUR - this is a ruling, not a preference
97
+ *
98
+ * The opposite arm was implemented and CLOSED (PR objectui#9144). The ruling's
99
+ * decisive axis: honouring the alias would have made
100
+ * *"8 predicate keys work while the rest stayed silently dropped - and partly
101
+ * working is harder to learn from than not working"*, because the author
102
+ * generalises "`props` is fine" and collides again on the next key with the
103
+ * cause now further away. The three measured readings behind it, each with a
104
+ * control that fires: `@objectstack/spec` already refuses `props` by name
105
+ * (`unrecognized_keys ["props"]`); `skills/objectui/rules/protocol.md` teaches
106
+ * it as an ERROR rather than as a gap; and the producer census found ZERO
107
+ * authored `props` bags across `examples/**` and `content/docs/**` (controls:
108
+ * `visibleWhen` 9 files, `type` 488 files), so there is no capability to cut.
109
+ *
110
+ * ## What it deliberately does NOT do
111
+ *
112
+ * It changes NO verdict and NO rendered byte. Every gate answers exactly what
113
+ * it answered before, nothing is hoisted, `schema.<KEY>` stays undefined for a
114
+ * renderer declared as `({ schema })`, and the bag reaches the element with the
115
+ * same keys it always did. The trap stops being SILENT; it does not stop being
116
+ * a trap, which is the same posture objectui#6708 took one card earlier and the
117
+ * only posture a diagnostic can take without becoming the honour arm the ruling
118
+ * refused.
119
+ *
120
+ * ## Why `console.error`, and why NOT dev-only
121
+ *
122
+ * Two established postures in this tree, and this leg needs a leg of each:
123
+ *
124
+ * - SEVERITY from `unevaluatedExpression.ts`, which is this repo's REFUSAL
125
+ * tier. Its neighbour {@link reportDroppedPropsBag} warns because it
126
+ * reports a value that was dropped; the ruling's word here is *refused*,
127
+ * and the sibling gate diagnostic states the split explicitly ("not the
128
+ * refusal `reportUnevaluatedExpressions` emits").
129
+ * - ALWAYS-ON from `visibilityDiagnostic.ts`'s unresolvable-predicate leg,
130
+ * which the maintainer took out of `__DEV__` in objectui#6038 (ruling
131
+ * 2026-08-25, option B: "the silence is no longer an accepted property")
132
+ * on the ground that a node gate which has stopped biting in production is
133
+ * a class-1 defect that must not be able to sit live and undiscovered.
134
+ * This is that class in its widest form - the gate never bit at all, on
135
+ * every render - and the ruling's own confidence gap is about exactly the
136
+ * population a `__DEV__` gate would silence: *"The producer census covers
137
+ * this repository's `examples/**`, `content/docs/**` and skills corpus.
138
+ * It does NOT measure authored metadata in production. If stored documents
139
+ * out there do carry `props`, those authors are already suffering the
140
+ * silent drop today."*
141
+ *
142
+ * The rate limit that makes an always-on console line affordable is the same
143
+ * one objectui#6038 required: the dedupe below is keyed on the MESSAGE, so the
144
+ * ceiling is one line per distinct authoring bug for the lifetime of the page,
145
+ * not one line per render and not one per node.
146
+ */
147
+ export declare const REFUSED_PROPS_PREDICATE_PREFIX = "[ObjectUI] A node-gate predicate under `props` is REFUSED";
148
+ /**
149
+ * Build the refusal message. Separate from the emit so a test can assert the
150
+ * words a developer is going to read, not merely that something was logged.
151
+ *
152
+ * Carries the MIGRATION LINE the ruling requires by name - *"the implementing
153
+ * round should pair the refusal with a named migration line in the diagnostic,
154
+ * not let half the keys quietly start working"*. It names the keys it refused
155
+ * and the two spellings that do work, and it names nothing else: there is no
156
+ * per-key canonical-alias table here, because inventing one would be a second
157
+ * declaration of ADR-0089's normalization competing with the spec's own.
158
+ */
159
+ export declare function formatRefusedPropsPredicateMessage(type: unknown, id: unknown, refusedKeys: readonly string[]): string;
160
+ /**
161
+ * Which node-gate predicate keys of the PARKED `props` bag are refused, or
162
+ * `null` when there is nothing to say.
163
+ *
164
+ * `nodeGateKeys` is passed IN rather than declared here: `SchemaRenderer` owns
165
+ * the two chain declarations and derives this union from them, so a leg added
166
+ * to either chain is covered by the same edit that adds it and this module
167
+ * never grows a twin list to drift from them (AGENTS.md #9).
168
+ *
169
+ * `parkedPropsBag` is the value of
170
+ * `propsWithoutCanonicalKeys(schema.props, schema.properties)` - the same
171
+ * subtraction the outgoing bag and the objectui#6708 diagnostic already use, so
172
+ * the two cases stay un-confused exactly as they are there: a key BOTH bags
173
+ * declare has already been subtracted (objectui#5123: `properties` wins, the
174
+ * author is getting the canonical answer and nothing is parked), while a key
175
+ * only `props` declares survives into that bag and is precisely the one no gate
176
+ * can see. A degenerate `props` contributes no keys through that same function,
177
+ * so `props: 'not-a-bag'` refuses nothing here - its defect is a different
178
+ * question (objectui#6752).
179
+ */
180
+ export declare function collectRefusedPropsPredicateKeys(type: unknown, nodeGateKeys: ReadonlySet<string>, parkedPropsBag: unknown): string[] | null;
181
+ /**
182
+ * Test-only reset for the dedupe above. Without it the second test to assert
183
+ * the same refusal reads the first test's dedupe entry and sees silence - a
184
+ * green run that checked nothing.
185
+ */
186
+ export declare function __resetRefusedPropsPredicateWarnings(): void;
187
+ /**
188
+ * Reports a node-gate predicate parked under `props`, in DEVELOPMENT AND IN
189
+ * PRODUCTION - read {@link REFUSED_PROPS_PREDICATE_PREFIX}'s docblock for why
190
+ * this leg carries neither the `__DEV__` gate nor the `console.warn` severity
191
+ * of its neighbour in this module.
192
+ *
193
+ * Returns the message it emitted (or `null`) so a caller or a test can read the
194
+ * decision rather than infer it from a spy.
195
+ */
196
+ export declare function reportRefusedPropsPredicate(type: unknown, id: unknown, nodeGateKeys: ReadonlySet<string>, parkedPropsBag: unknown): string | null;