@object-ui/fields 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 (50) hide show
  1. package/CHANGELOG.md +5436 -0
  2. package/README.md +108 -8
  3. package/dist/FieldEditWidget.d.ts +54 -6
  4. package/dist/coerceToSafeValue.d.ts +54 -0
  5. package/dist/currency.d.ts +1 -1
  6. package/dist/index.cjs +5 -5
  7. package/dist/index.css +26 -2
  8. package/dist/index.d.ts +229 -57
  9. package/dist/index.js +3811 -3009
  10. package/dist/widgets/AvatarField.d.ts +19 -3
  11. package/dist/widgets/BooleanField.d.ts +1 -1
  12. package/dist/widgets/DateField.d.ts +1 -1
  13. package/dist/widgets/DateTimeField.d.ts +1 -1
  14. package/dist/widgets/FilterConditionField.d.ts +21 -7
  15. package/dist/widgets/GeolocationField.d.ts +9 -3
  16. package/dist/widgets/GridField.d.ts +63 -99
  17. package/dist/widgets/LocationField.d.ts +30 -3
  18. package/dist/widgets/LookupField.d.ts +8 -0
  19. package/dist/widgets/NumberField.d.ts +1 -1
  20. package/dist/widgets/ObjectField.d.ts +9 -2
  21. package/dist/widgets/OptionsEmptyState.d.ts +29 -5
  22. package/dist/widgets/PasswordField.d.ts +14 -0
  23. package/dist/widgets/PeoplePicker.d.ts +17 -2
  24. package/dist/widgets/PercentField.d.ts +2 -1
  25. package/dist/widgets/PersonRow.d.ts +13 -2
  26. package/dist/widgets/RecipientPickerField.d.ts +43 -0
  27. package/dist/widgets/RecordPickerDialog.d.ts +31 -12
  28. package/dist/widgets/RichTextField.d.ts +1 -1
  29. package/dist/widgets/SelectionTray.d.ts +13 -2
  30. package/dist/widgets/TextField.d.ts +1 -1
  31. package/dist/widgets/TimeField.d.ts +1 -1
  32. package/dist/widgets/file-affordance.d.ts +33 -0
  33. package/dist/widgets/file-size-guard.d.ts +5 -23
  34. package/dist/widgets/file-value.d.ts +38 -9
  35. package/dist/widgets/lookupColumnDisplay.d.ts +24 -7
  36. package/dist/widgets/nativeDateValue.d.ts +9 -27
  37. package/dist/widgets/number-format.d.ts +137 -0
  38. package/dist/widgets/numberBadInput.d.ts +135 -0
  39. package/dist/widgets/personDisplay.d.ts +22 -4
  40. package/dist/widgets/richTextDisplay.d.ts +36 -0
  41. package/dist/widgets/toDomProps.d.ts +10 -1
  42. package/dist/widgets/toHostGroupProps.d.ts +53 -0
  43. package/dist/widgets/toHostProps.d.ts +105 -0
  44. package/dist/widgets/types.d.ts +11 -3
  45. package/dist/widgets/upload-error-message.d.ts +6 -0
  46. package/dist/widgets/uploadingScope.d.ts +142 -0
  47. package/dist/widgets/useCascadingOptions.d.ts +3 -2
  48. package/dist/widgets/useRecordQuery.d.ts +4 -2
  49. package/dist/widgets/useUploadingSignal.d.ts +8 -0
  50. package/package.json +11 -12
package/dist/index.css CHANGED
@@ -104,6 +104,9 @@
104
104
  .min-w-\[20px\] {
105
105
  min-width: 20px;
106
106
  }
107
+ .shrink {
108
+ flex-shrink: 1;
109
+ }
107
110
  .basis-\[9rem\] {
108
111
  flex-basis: 9rem;
109
112
  }
@@ -127,6 +130,9 @@
127
130
  .gap-y-1 {
128
131
  row-gap: var(--spacing, 0.25rem);
129
132
  }
133
+ .rounded-\[2px\] {
134
+ border-radius: 2px;
135
+ }
130
136
  .border-\[color\:var\(--os-badge-border\)\] {
131
137
  border-color: var(--os-badge-border);
132
138
  }
@@ -178,6 +184,9 @@
178
184
  .border-purple-200 {
179
185
  border-color: var(--color-purple-200, oklch(90.2% 0.063 306.703));
180
186
  }
187
+ .border-red-200 {
188
+ border-color: var(--color-red-200, oklch(88.5% 0.062 18.334));
189
+ }
181
190
  .border-red-500 {
182
191
  border-color: var(--color-red-500, oklch(63.7% 0.237 25.331));
183
192
  }
@@ -214,12 +223,21 @@
214
223
  background-color: color-mix(in oklab, var(--color-black, #000) 50%, transparent);
215
224
  }
216
225
  }
226
+ .bg-blue-50 {
227
+ background-color: var(--color-blue-50, oklch(97% 0.014 254.604));
228
+ }
229
+ .bg-blue-500 {
230
+ background-color: var(--color-blue-500, oklch(62.3% 0.214 259.815));
231
+ }
217
232
  .bg-destructive\/5 {
218
233
  background-color: color-mix(in srgb, hsl(var(--destructive)) 5%, transparent);
219
234
  @supports (color: color-mix(in lab, red, red)) {
220
235
  background-color: color-mix(in oklab, var(--color-destructive, hsl(var(--destructive))) 5%, transparent);
221
236
  }
222
237
  }
238
+ .bg-gray-50 {
239
+ background-color: var(--color-gray-50, oklch(98.5% 0.002 247.839));
240
+ }
223
241
  .bg-gray-200 {
224
242
  background-color: var(--color-gray-200, oklch(92.8% 0.006 264.531));
225
243
  }
@@ -280,12 +298,15 @@
280
298
  .px-px {
281
299
  padding-inline: 1px;
282
300
  }
283
- .py-8 {
284
- padding-block: calc(var(--spacing, 0.25rem) * 8);
301
+ .pt-3 {
302
+ padding-top: calc(var(--spacing, 0.25rem) * 3);
285
303
  }
286
304
  .pr-1\.5 {
287
305
  padding-right: calc(var(--spacing, 0.25rem) * 1.5);
288
306
  }
307
+ .pr-8 {
308
+ padding-right: calc(var(--spacing, 0.25rem) * 8);
309
+ }
289
310
  .pl-0\.5 {
290
311
  padding-left: calc(var(--spacing, 0.25rem) * 0.5);
291
312
  }
@@ -331,6 +352,9 @@
331
352
  .text-red-600 {
332
353
  color: var(--color-red-600, oklch(57.7% 0.245 27.325));
333
354
  }
355
+ .text-white {
356
+ color: var(--color-white, #fff);
357
+ }
334
358
  .text-yellow-800 {
335
359
  color: var(--color-yellow-800, oklch(47.6% 0.114 61.907));
336
360
  }
package/dist/index.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import { default as React } from 'react';
2
2
  import { FieldMetadata } from '@object-ui/types';
3
- import { humanizeLabel, ComponentMeta } from '@object-ui/core';
3
+ import { PercentScale, humanizeLabel, formatDate, formatDateTime, formatDateTimeCompactParts, formatRelativeDate, ComponentMeta, DateDisplayOptions } from '@object-ui/core';
4
+ import { formatCurrency, percentCellScale } from './widgets/number-format.js';
5
+ import { coerceToSafeValue } from './coerceToSafeValue.js';
4
6
  import { resolveFieldCurrency } from './currency.js';
5
7
  /**
6
8
  * Pick the most reasonable display name from an arbitrary record object.
@@ -9,8 +11,18 @@ import { resolveFieldCurrency } from './currency.js';
9
11
  export declare function pickRecordDisplayName(record: Record<string, unknown> | null | undefined, preferredField?: string): string | undefined;
10
12
  /**
11
13
  * Heuristic: detect strings that look like opaque foreign-key IDs (e.g. nanoid
12
- * or BSON ObjectId). Used so we don't display random gibberish to users when
13
- * a lookup wasn't expanded.
14
+ * or BSON ObjectId).
15
+ *
16
+ * ⛔ Nothing in this repo calls it any more, and re-introducing a caller that
17
+ * decides PRESENTATION from it would re-open objectui#8695. It used to gate
18
+ * `LookupCellRenderer`'s muted `—`, i.e. it decided how an unresolved
19
+ * reference was drawn from the SHAPE of the string rather than from whether
20
+ * anything resolved — so `'Ada Lovelace'` printed as a confident name and an
21
+ * opaque id lost its value, two opposite answers to one epistemic state. The
22
+ * export is kept because it is part of this package's published surface and
23
+ * retiring it is a breaking change no display card is entitled to make; the
24
+ * shape question itself is legitimate (a picker filter, an id-vs-name guess),
25
+ * it is only an answer to "what did this screen resolve?" that it can never be.
14
26
  */
15
27
  export declare function isLikelyOpaqueId(v: unknown): boolean;
16
28
  /**
@@ -22,15 +34,18 @@ export interface CellRendererProps {
22
34
  isEditing?: boolean;
23
35
  onChange?: (value: any) => void;
24
36
  }
37
+ export { coerceToSafeValue };
38
+ export { resolveFieldCurrency };
25
39
  /**
26
- * Coerce a value to a safe primitive for rendering.
27
- * Handles MongoDB wrapper types ($numberDecimal, $oid, $date), expanded
28
- * reference objects, and arrays so that no raw object is ever passed as
29
- * a React child — preventing React error #310.
40
+ * `formatCurrency` lives beside `formatNumberFieldValue` in
41
+ * `./widgets/number-format.ts`, re-exported here under its long-standing
42
+ * `@object-ui/fields` name: `CurrencyField`'s read-only branch calls the same
43
+ * function `CurrencyCellRenderer` below calls, and the lazily loaded widget
44
+ * cannot import this barrel (objectui#11444). The width (the currency's ISO
45
+ * 4217 minor-unit count, every amount, no whole-amount trimming) and its
46
+ * history are documented there.
30
47
  */
31
- export declare function coerceToSafeValue(value: unknown): string | number | boolean | null | undefined;
32
- export { resolveFieldCurrency };
33
- export declare function formatCurrency(value: number, currency?: string, locale?: string): string;
48
+ export { formatCurrency };
34
49
  /**
35
50
  * Format currency value in compact form for mobile display.
36
51
  * E.g., $150,000 → $150K, $1,200,000 → $1.2M
@@ -38,18 +53,36 @@ export declare function formatCurrency(value: number, currency?: string, locale?
38
53
  */
39
54
  export declare function formatCompactCurrency(value: number, currency?: string, locale?: string): string;
40
55
  /**
41
- * Format a plain number with thousands separators, no currency symbol.
42
- * Used as a safe fallback when a currency-typed field has no `currency`
43
- * configured — we'd rather render `1,234.50` than silently assume USD.
56
+ * Format a plain number with thousands separators at a fixed display width, no
57
+ * currency symbol. `formatCurrency` renders an amount with no currency resolved
58
+ * through the same call shape (`./widgets/number-format.ts`): we'd rather
59
+ * render `1,234.50` than silently assume USD.
44
60
  */
45
61
  export declare function formatNumber(value: number, decimals?: number, locale?: string): string;
46
62
  /**
47
- * Format percent value.
48
- * Handles both decimal (0.8 = 80%) and whole number (80 = 80%) inputs.
63
+ * `percentCellScale` lives beside `formatPercentPoints` in
64
+ * `./widgets/number-format.ts` and is re-exported here: the storage scale the
65
+ * percent cell reads a field at, so every face rendering the cell's number for
66
+ * the same field reads the same answer by reference (objectui#11475).
67
+ */
68
+ export { percentCellScale };
69
+ /**
70
+ * Format a stored percentage at the storage the caller STATES.
49
71
  *
50
- * `locale` is the third positional parameter, matching {@link formatNumber} and
51
- * {@link formatCurrency} — the shape the sibling formatters already use.
52
- * Callers should pass the tag from `useDisplayLocale()`.
72
+ * `percentScale` is REQUIRED (objectui#11475): `'fraction'` (0.8 means 80%) or
73
+ * `'whole'` (80 means 80%), the spec's `PercentScale`. A face holding a field
74
+ * reads it with {@link percentCellScale} (the spec's `percentScaleOf`, a
75
+ * fraction unless the field declares a `max` above 1); a face without one
76
+ * states what it knows. A value outside the union throws. This used to guess
77
+ * from the value's magnitude (`percentDisplayValue`'s old body), so a
78
+ * fraction-stored `1` read `1%` here and `100%` in the read-only form.
79
+ *
80
+ * FROM `formatPercent(value, precision, locale)` TO
81
+ * `formatPercent(value, percentScale, precision, locale)`.
82
+ *
83
+ * `locale` is the last positional parameter, matching {@link formatNumber} and
84
+ * {@link formatCurrency}. Callers should pass the tag from
85
+ * `useDisplayLocale()`.
53
86
  *
54
87
  * Before objectui#4553 this function took no locale and never touched `Intl`:
55
88
  * its whole body was `${percentDisplayValue(value).toFixed(precision)}%`, so it
@@ -59,7 +92,7 @@ export declare function formatNumber(value: number, decimals?: number, locale?:
59
92
  * so the grouping and the locale are fixed together: en output MOVES from
60
93
  * `1235%` to `1,235%` at four digits and up, and that move is the fix.
61
94
  */
62
- export declare function formatPercent(value: number, precision?: number, locale?: string): string;
95
+ export declare function formatPercent(value: number, percentScale: PercentScale, precision?: number, locale?: string): string;
63
96
  /**
64
97
  * Humanize a snake_case or kebab-case string into Title Case — the fallback
65
98
  * label when no explicit `option.label` exists.
@@ -76,41 +109,20 @@ export declare function formatPercent(value: number, precision?: number, locale?
76
109
  * `humanizeFieldKey`'s camelCase-splitting KEY convention.
77
110
  */
78
111
  export { humanizeLabel };
79
- /** Options shared by {@link formatDate} / {@link formatRelativeDate}. */
80
- export interface DateDisplayOptions {
81
- dueLike?: boolean;
82
- /** BCP-47 display locale (ADR-0053 tenant default); falls back to the runtime locale. */
83
- locale?: string;
84
- /** i18n translate fn for phrases `Intl` can't produce (the "Overdue Nd" wording). */
85
- t?: (key: string, params?: Record<string, unknown>) => string;
86
- }
87
- /**
88
- * Format date as relative time (e.g., "3 days ago", "Today", "Overdue 3d"),
89
- * localized via `Intl.RelativeTimeFormat` (objectstack-ai/objectstack#3040).
90
- *
91
- * `dueLike` gates the "Overdue" wording — a past `start_date`/`created_at`
92
- * isn't overdue, only a past due/deadline-semantic field is. Non-due-like
93
- * past dates render as plain "N days ago" instead. The overdue phrase has no
94
- * `Intl` equivalent, so it resolves through `options.t` (key
95
- * `fields.relativeDate.overdue`) with an English fallback.
96
- */
97
- export declare function formatRelativeDate(value: string | Date | number, options?: DateDisplayOptions): string;
98
112
  /**
99
- * Format date value
100
- */
101
- export declare function formatDate(value: string | Date | number, style?: string, options?: DateDisplayOptions): string;
102
- /**
103
- * Format datetime value.
113
+ * The date/datetime display path - `@object-ui/core`'s `utils/date-display.ts`
114
+ * (objectui#7178), re-exported here under its original names so every existing
115
+ * consumer of `@object-ui/fields` is unchanged.
104
116
  *
105
- * `options` mirrors {@link formatDate}'s and is optional, so an existing
106
- * caller that passes nothing keeps the exact runtime-default behavior it had.
107
- * Before objectui#4272 the parameter did not exist at all, which meant no
108
- * caller could localize this function however hard it tried — it always handed
109
- * `Intl` an `undefined` tag, i.e. the MACHINE's locale, which is neither of
110
- * the repo's two locale channels. Callers should pass the tag from
111
- * `useDisplayLocale()`.
117
+ * It moved for the reason `formatDisplayNumber` moved in objectui#4576: this
118
+ * barrel is a React package, and the React-free engine could not import from
119
+ * it. `core`'s `formatMeasure` needed this exact path - a dataset measure over
120
+ * a date field was rendering its raw ISO string - and the alternative to
121
+ * moving was a second date convention in `dataset-format.ts`, which is the
122
+ * drift #4576 already paid for once with percent.
112
123
  */
113
- export declare function formatDateTime(value: string | Date | number, options?: DateDisplayOptions): string;
124
+ export { formatDate, formatDateTime, formatDateTimeCompactParts, formatRelativeDate };
125
+ export type { DateDisplayOptions };
114
126
  /**
115
127
  * Text field cell renderer
116
128
  */
@@ -140,7 +152,7 @@ export declare function DateCellRenderer({ value, field }: CellRendererProps): R
140
152
  /**
141
153
  * DateTime field cell renderer (Airtable-style with date and time visually separated)
142
154
  */
143
- export declare function DateTimeCellRenderer({ value }: CellRendererProps): React.ReactElement;
155
+ export declare function DateTimeCellRenderer({ value, field }: CellRendererProps): React.ReactElement;
144
156
  /** The six pill colors plus the two dot colors derived from one declared hex. */
145
157
  export interface HexBadgePalette {
146
158
  bg: string;
@@ -245,14 +257,19 @@ export declare function ImageCellRenderer({ value }: CellRendererProps): React.R
245
257
  * Display order:
246
258
  * 1. Embedded record object (`{ id, name, ... }` from `$expand`) → use its name
247
259
  * 2. Static `field.options[]` (e.g. when the lookup is a closed enum) → look up label
248
- * 3. Fetch-on-demand: when the value is a primitive ID and `field.reference_to`
260
+ * 3. Fetch-on-demand: when the value is a primitive ID and `field.reference`
249
261
  * is known, resolve via dataSource and show the related record's display name.
250
- * Falls back to a muted placeholder while pending and on failure.
262
+ * 4. Nothing named it → the unresolved-reference affordance (objectui#8695):
263
+ * the raw value, kept visible, beside a stated epistemic marker. This arm
264
+ * used to be two — a muted `—` for opaque-LOOKING strings and confident
265
+ * bare text for everything else — which answered one state two ways.
251
266
  *
252
267
  * Record → name resolution (1 and 3) goes through the referenced object's
253
268
  * schema when the data source exposes it (`displayField` → nameField/titleFormat
254
269
  * → derivation, see {@link resolveLookupRecordName}), so the chip and the
255
- * picker agree (issue #2357).
270
+ * picker agree (issue #2357) — and, like the picker's option label, it reads
271
+ * only the fields the viewer may read on the referenced object
272
+ * (objectui#10501).
256
273
  */
257
274
  export declare function LookupCellRenderer({ value, field }: CellRendererProps): React.ReactElement;
258
275
  /**
@@ -261,8 +278,16 @@ export declare function LookupCellRenderer({ value, field }: CellRendererProps):
261
278
  export declare function FormulaCellRenderer({ value }: CellRendererProps): React.ReactElement;
262
279
  /**
263
280
  * User/Owner field cell renderer (with avatars)
281
+ *
282
+ * The name and the avatar are DISPLAY values, so each person row is drawn as
283
+ * the viewer may read it (objectui#10535): with a loaded policy, the fields it
284
+ * denies on the person's object are removed first ({@link withoutDeniedFields},
285
+ * `id` kept), and everything below reads that row — exactly as it reads the
286
+ * row a stripping backend serves. The person's object is the field's
287
+ * `reference`, and `sys_user` when it names none: the object `UserField`
288
+ * points the picker at.
264
289
  */
265
- export declare function UserCellRenderer({ value }: CellRendererProps): React.ReactElement;
290
+ export declare function UserCellRenderer({ value, field }: CellRendererProps): React.ReactElement;
266
291
  /**
267
292
  * Register a custom field renderer
268
293
  * @param type Field type (e.g. 'text', 'location', 'my-custom-type')
@@ -293,12 +318,13 @@ export declare function ColorSwatchCellRenderer({ value }: CellRendererProps): R
293
318
  * The rich-content display pipelines — `markdown` through the GFM renderer,
294
319
  * `html`/`richtext` through the sanitizing HTML renderer — live in
295
320
  * `./widgets/richTextDisplay.js` rather than here, so `RichTextField` can
296
- * import them without importing this barrel back (objectui#5498). Re-exported
321
+ * import them without importing this barrel back (`4bb940b6e`). Re-exported
297
322
  * unchanged: they are part of this package's published surface, and
298
323
  * `RICH_TEXT_CELL_RENDERERS` below is the one table both the cell resolver and
299
324
  * the widget's readonly branch read.
300
325
  */
301
326
  export { MarkdownCellRenderer, HtmlCellRenderer } from './widgets/richTextDisplay.js';
327
+ export { RICH_TEXT_FIELD_TYPES, type RichTextFieldType } from './widgets/richTextDisplay.js';
302
328
  /**
303
329
  * Renders a `location`/`geolocation` value as readable coordinates with a pin.
304
330
  * Accepts `{ lat, lng }` / `{ latitude, longitude }`, a `"lat,lng"` string,
@@ -325,6 +351,136 @@ export declare function LocationCellRenderer({ value }: CellRendererProps): Reac
325
351
  * is never silently swallowed by the fix.
326
352
  */
327
353
  export declare function AddressCellRenderer({ value }: CellRendererProps): React.ReactElement;
354
+ /**
355
+ * `password` / `secret` cell renderer: the mask for a credential that is SET,
356
+ * and the shared affordance for one that is NOT (objectui#8678).
357
+ *
358
+ * The entry this replaces was an argument-less arrow that drew the mask for
359
+ * every input, `null` included. On screen, a credential that was never set was
360
+ * then drawn exactly like one that is set. The platform's read contract is
361
+ * presence-preserving on purpose: `@objectstack/spec`'s `SECRET_MASK` docblock
362
+ * says an unset credential "reads back `null` instead, never this mask", which
363
+ * is what lets a console render "configured" vs "not configured" at all. This
364
+ * cell threw that distinction away one step before the reader saw it.
365
+ *
366
+ * ⛔ The value is NEVER printed and never reaches the DOM. Only its presence is
367
+ * read. A populated value keeps the exact mask it drew before this card, and
368
+ * nothing here is a weaker mask.
369
+ *
370
+ * "Empty" is the string class's answer, since both types are
371
+ * `STRING_VALUE_TYPES` members in the spec: `TextCellRenderer`'s predicate
372
+ * exactly, the floor on the coerced text. `{}` therefore stays a VALUE and
373
+ * keeps the mask, per objectui#8596's string-class ruling ("the record IS
374
+ * storing something"). A whitespace-only string also keeps it, because `text`
375
+ * keeps its spaces and a blank credential is still a set one.
376
+ * `cellRenderers.valueIndependent-8678` pins this byte-equal to `text` for
377
+ * every empty input.
378
+ *
379
+ * ⭐ A NAMED module-level component, for the reason {@link RepeaterCellRenderer}
380
+ * states: `EmptyValue` holds a hook, and an inline arrow in the table below
381
+ * is a new component type on every resolution, so it would tear that hook down
382
+ * per render.
383
+ *
384
+ * Exported (objectui#10657) for the cell a producer cannot type yet. While an
385
+ * object-bound table is still waiting for its object's field types, or after
386
+ * that read failed, a column with no type of its own could be a credential,
387
+ * so `ObjectGrid`, `RelatedList` and `ObjectDataTable` draw it WITHHELD: as
388
+ * this mask, never as text. It is this component and not the live registry
389
+ * entry for `password`, so a host override of a masked type cannot change what
390
+ * a withheld cell draws. Registering it for a type
391
+ * (`registerFieldRenderer('api_token', MaskedCellRenderer)`) masks that type,
392
+ * as {@link isMaskedFieldType} describes. `field` is optional: the mask reads
393
+ * only whether the value is present, so a withheld cell has no field metadata
394
+ * to invent.
395
+ */
396
+ export declare function MaskedCellRenderer({ value, }: Omit<CellRendererProps, 'field'> & {
397
+ field?: CellRendererProps['field'];
398
+ }): React.ReactElement;
399
+ /**
400
+ * The field types this package DECLARES masked: their standard cell is
401
+ * {@link MaskedCellRenderer}, which draws `••••••` in place of the value
402
+ * (objectui#8686). The read-side twin of the credential entry in
403
+ * `INLINE_EXCLUDED_FIELD_TYPES`, which lives in `FieldEditWidget`.
404
+ *
405
+ * ⭐ This set BUILDS the table's masked entries — `buildStandardCellRendererMap`
406
+ * spreads one {@link MaskedCellRenderer} entry per member — so adding a type
407
+ * here masks its cell AND makes {@link isMaskedFieldType} answer `true` for it,
408
+ * in one edit. There is no second list to keep in step.
409
+ *
410
+ * ⚠️ Membership answers "what ships masked", ⛔ not "what is masked right now":
411
+ * `registerFieldRenderer` can add or replace a masked type at runtime, and a
412
+ * set cannot see that. Consumers deciding what to do with a cell's value ask
413
+ * {@link isMaskedFieldType}.
414
+ *
415
+ * RAW spellings, deliberately: the cell path does not resolve form aliases
416
+ * (`getCellRenderer` is an exact-key lookup), so `field:password` renders in
417
+ * the clear and is not a member.
418
+ *
419
+ * Owned here for now. The objectui#8686 ruling made the protocol the first
420
+ * place to look: if `@objectstack/spec` comes to declare which field types are
421
+ * credentials, this set derives from that declaration instead of listing types.
422
+ */
423
+ export declare const MASKED_FIELD_TYPES: ReadonlySet<string>;
424
+ /**
425
+ * Is a cell of this field type drawn as a mask instead of as its value? The
426
+ * one authority for that question (objectui#8686): ask it rather than keep a
427
+ * list of types. The read-side twin of `isInlineExcludedFieldType()`.
428
+ *
429
+ * A LIVE reading of the cell registry, taken at call time, in the order
430
+ * {@link getCellRenderer} resolves: the runtime registry first, then the
431
+ * standard table.
432
+ *
433
+ * 1. The type resolves to THE mask ({@link MaskedCellRenderer}) → `true`,
434
+ * declared or not. That is how a host adds a masked type:
435
+ * `registerFieldRenderer('api_token', getCellRenderer('password'))`.
436
+ * 2. Otherwise, a type outside {@link MASKED_FIELD_TYPES} → `false`.
437
+ * 3. A declared type whose mask a host REPLACED at runtime
438
+ * (`registerFieldRenderer('password', X)`) → the override is read:
439
+ * - X is one of this package's own cell renderers → `false`. None of them
440
+ * is the mask, so the predicate answers `false` and the cell draws what
441
+ * X draws (for `TextCellRenderer`, the value).
442
+ * - X is the host's own component → `true`, the declared answer. Nothing
443
+ * here can tell whether an opaque component masks. Answering `false`
444
+ * would offer a credential to anything that trusts this predicate
445
+ * (the detail page's copy affordance, objectui#8440) while a custom mask
446
+ * hides it on screen. So an unreadable override falls back to the
447
+ * declared set, on the side that withholds.
448
+ *
449
+ * RAW spelling, no alias resolution, for the reason {@link MASKED_FIELD_TYPES}
450
+ * states. Side-effect free, like `isInlineExcludedFieldType()`: unlike
451
+ * {@link getCellRenderer}, it never reports a retired spelling.
452
+ */
453
+ export declare function isMaskedFieldType(fieldType: string | undefined): boolean;
454
+ /**
455
+ * Every field type that resolves to a cell renderer OF ITS OWN — a LIVE
456
+ * reading of the registry, taken when you call it (objectui#8734).
457
+ *
458
+ * ## Why this exists
459
+ *
460
+ * Two censuses — `cellRenderers.objectLiteral-8596` in this package and
461
+ * `summaryChip.badgeFitCensus-8464` in `@object-ui/plugin-detail` — declare in
462
+ * prose that they measure "every type `getCellRenderer` resolves to a renderer
463
+ * of its own", and each enforced that declaration with a hard-coded population
464
+ * size. A newly registered type is absent from such a table, so every row in it
465
+ * still passes: the census goes on measuring a snapshot while claiming to
466
+ * measure the registry. This function is what those censuses reconcile against,
467
+ * so a new type turns a silent pass into a red row that NAMES the type.
468
+ *
469
+ * ## ⭐ A function, ⛔ not a frozen constant
470
+ *
471
+ * `FORM_FIELD_TYPES` next door may be `Object.freeze(Object.keys(...))` because
472
+ * `fieldWidgetMap` is a module literal that never changes. This registry DOES
473
+ * change: {@link registerFieldRenderer} is published, and a host may add or
474
+ * override a type after this module is evaluated. A frozen constant here would
475
+ * be a snapshot taken at import time — the very defect the censuses are being
476
+ * repaired for, one level down. Every call re-reads.
477
+ *
478
+ * The answer is the UNION of the runtime registry and the standard table,
479
+ * because {@link getCellRenderer} dispatches on both; it is sorted so callers
480
+ * get a stable order, and `TextCellRenderer` is the TOTAL fallback for
481
+ * everything else, which is exactly why "everything else" is not listed here.
482
+ */
483
+ export declare function listCellRendererTypes(): readonly string[];
328
484
  /**
329
485
  * Get the appropriate cell renderer for a field type
330
486
  */
@@ -694,6 +850,17 @@ export declare function registerField(fieldType: string): void;
694
850
  * nothing is silently substituted either, which is the whole point: the author
695
851
  * sees a refusal where they expected an input, not a text box that looks like
696
852
  * it worked.
853
+ *
854
+ * The spelling it names is `retiredFieldType` — the key the RESOLVER answered
855
+ * with this tombstone — and is re-derived from the field only when a host
856
+ * renders the tombstone directly without one (objectui#10471). The field is
857
+ * the wrong witness whenever the retired spelling arrived through a different
858
+ * key than `field.type`: an authored `widget` wins over `type` in both form
859
+ * hosts, so `{ type: 'user', widget: 'owner' }` reaches the `owner` tombstone
860
+ * carrying a `user` field, and deriving from it named the LIVE type as
861
+ * retired, lost the prescription, and logged nothing. The two resolvers fill
862
+ * the prop by binding it ({@link retiredFieldTombstoneFor}); a host whose key
863
+ * IS `field.type` (plugin-detail's inline editor) may keep omitting it.
697
864
  */
698
865
  export declare const RetiredFieldTombstone: React.FC<Record<string, any>>;
699
866
  export declare function registerAllFields(): void;
@@ -751,4 +918,9 @@ export * from './widgets/TagsField.js';
751
918
  export { withFieldCarrier } from './withFieldCarrier.js';
752
919
  export { toDomProps } from './widgets/toDomProps.js';
753
920
  export type { DomProps } from './widgets/toDomProps.js';
921
+ export { toHostProps } from './widgets/toHostProps.js';
922
+ export type { HostProps } from './widgets/toHostProps.js';
754
923
  export { toDateInputValue, toDateTimeInputValue, fromDateTimeInputValue, } from './widgets/nativeDateValue.js';
924
+ export { useUploadingScope, UploadingScopeProvider } from './widgets/uploadingScope.js';
925
+ export { useUploadingSignal } from './widgets/useUploadingSignal.js';
926
+ export type { UploadingScope } from './widgets/uploadingScope.js';