@burojs/react 0.4.0 → 0.5.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.
package/dist/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
 
2
- import { Action, ActionDI, ActionDataAccess, ActionResult, AppContext, AppDefinition, AppRuntime, ApplicationSnapshot, Audit, AuditFieldDiff, AuthCheckResult, AuthProvider, AuthResult, Availability, BatchedQueryOperation, BulkActionHandler, BulkTarget, Capability, CapabilityDecision, Check, CheckOutcome, CheckSurface, CountDescriptor, CountPolicy, CreateManyDescriptor, CreateOneDescriptor, CustomMutationHandle, CustomQueryHandle, DataError, DataProvider, DataProviderSessionSnapshot, DataQueryKey, DataSessionController, DataSessionController as DataSessionController$1, DataSessionSnapshot, DataSessionState, DataSessionTransitionParticipant, DataSessionTransitionParticipant as DataSessionTransitionParticipant$1, DeleteManyDescriptor, DeleteOneDescriptor, Dispatcher, DocContent, DocsConfig, DocumentTemplate, EntityRef, EnumValue, ErasedMutationDescriptor, ErasedQueryDescriptor, ErasedResourceHandle, ExportFormat, ExportHandle, ExportRequest, Exporter, FieldContextFrames, FieldDeclaration, FieldDefBase, FieldIssue, FieldOption, FilterExpression, FilterGroup, FilterPredicateOp, FixedFilterInput, I18nCaps, InferCreateInput, InferCustomInput, InferCustomOutput, InferUpdateInput, ItemActionPlacement, LayerRuntime, LayerStatus, ListDescriptor, LivePlan, ManifestResolution, ManyDescriptor, MessageReference, MutationResultFor, OneDescriptor, PermissionMap, PortableObject, PortableProjection, PortableSnapshotManifest, PortableValue, QueryContext, QueryResultFor, RecordCodecError, RecordCodecOutcome, RegistryResource, RequestContext, ResolvedDocTopic, ResolvedFixedFilter, ResourceDef, ResourceFieldDefinition, ResourceFieldMap, ResourceHandle, ResourceRecordCodec, ResourceRefInput, RuleContext, SavedViewDef, SnapshotEntityTargetInput, SortExpression, StandardSchemaV1, TaskRunFailure, TaskRunReport, UIActivationContext, UIActivationSurface, UIPresentRequest, UIPresentResult, UIPresentationDescriptor, UIPresentationRuntime, UiSettingCardinality, UiSettingCollection, UiSettingEntryPermissions, UiSettingScope, UiSettingSingleton, UiSettingTypeDefinition, UiSettingsAdapter, UiSettingsIssue, UiSettingsSnapshot, UiSettingsStore, UpdateManyDescriptor, UpdateOneDescriptor, ValidationContext, ValidationIssueSeverity, ValidationIssues, createDataSession, createResourceRecordCodec, normalizeServerRecordIssues } from "@burojs/core";
2
+ import { _ as InlineFlowHolder, a as FormSubmitResult, c as FieldBinding, d as ResolvedIssue, f as CheckGateRequest, g as PlacementRuntime, h as useCheckGate, i as FormSteps, l as FormValues, m as UseCheckGateResult, n as FormControllerFailure, o as UseFormControllerFormOptions, p as CheckGateState, r as FormDraftState, s as UseFormControllerRecordOptions, t as FormController, u as RelationFilterOpt, v as InlineLease } from "./types-DH3beUOO.js";
3
+ import { ActionDI, ActionDataAccess, ActionFlow, AnyFrameHandle, AppAuditEventInput, AppContext, AppDefinition, AppRuntime, ApplicationSnapshot, Audit, AuthCheckResult, AuthProvider, AuthResult, Availability, BatchedQueryOperation, BlockLookup, BulkTarget, Capability, CapabilityDecision, CheckOutcome, CheckSurface, CompiledFlow, CompiledScreen, CountDescriptor, CountPolicy, CreateOneDescriptor, CustomMutationHandle, CustomQueryHandle, DataError, DataProvider, DataProviderSessionSnapshot, DataQueryKey, DataSessionController, DataSessionController as DataSessionController$1, DataSessionSnapshot, DataSessionState, DataSessionTransitionParticipant, DataSessionTransitionParticipant as DataSessionTransitionParticipant$1, DeleteManyDescriptor, DeleteOneDescriptor, Dispatcher, DispatcherEffects, DocContent, DocsConfig, EntityRef, EnumValue, ErasedMutationDescriptor, ErasedQueryDescriptor, ErasedResourceHandle, ExportFormat, ExportHandle, ExportRequest, Exporter, FieldAccessMap, FieldContextSources, FieldDeclaration, FieldDefBase, FieldOption, FieldReach, FilterExpression, FilterGroup, FilterPredicateOp, FixedFilterInput, FlowControlState, FlowGateAnswer, FlowRef, FlowResult, FlowRun, FlowTarget, FrameHandle, FrameKind, I18nCaps, InferBlockData, InferBlockProps, InferCreateInput, InferCustomInput, InferCustomOutput, InferSchemaOutput, InferUpdateInput, ItemActionPlacement, LayerRuntime, LayerStatus, LayoutSize, ListAccess, ListDescriptor, LivePlan, ManifestResolution, ManyDescriptor, MutationResultFor, NodeState, OneDescriptor, PageRequires, PanelTone, PermissionMap, PlanScreenInput, PortableObject, PortableProjection, PortableSnapshotManifest, PortableValue, QueryContext, QueryResultFor, QuickHost, RecordCodecError, RecordCodecOutcome, RegistryReader, RegistryResource, RelationCandidate, RequestContext, ResolvedDocTopic, ResolvedFixedFilter, ResourceDef, ResourceFieldMap, ResourceHandle, ResourceRecordCodec, ResourceRefInput, RouteDescriptor as RouteDescriptor$1, Row, RuleContext, SavedViewDef, ScreenMode, ScreenPlan, ScreenSources, SearchDecl, SlotMap, SnapshotEntityTargetInput, SortExpression, SourceState, StandardSchemaV1, TaskRunFailure, TaskRunReport, UIActivationContext, UIActivationSurface, UIPresentAnchor, UIPresentHost, UIPresentRequest, UIPresentResult, UIPresentationRuntime, UiSettingCardinality, UiSettingCollection, UiSettingEntryPermissions, UiSettingScope, UiSettingSingleton, UiSettingTypeDefinition, UiSettingsAdapter, UiSettingsIssue, UiSettingsSnapshot, UiSettingsStore, UpdateManyDescriptor, UpdateOneDescriptor, ValidationIssue, ValidationIssues, createDataSession, createResourceRecordCodec, normalizeServerRecordIssues } from "@burojs/core";
3
4
  import * as React from "react";
4
- import { ComponentType, ReactElement, ReactNode } from "react";
5
+ import { ComponentType, Context, ReactElement, ReactNode, RefObject } from "react";
5
6
  import { Query, QueryClient, UseMutationOptions, UseMutationResult, UseQueryOptions, UseQueryResult } from "@tanstack/react-query";
6
7
  import { I18nFacade, ResourceI18n } from "@burojs/i18n-core";
7
8
  import { FileRef, FileStorageAdapter, ResolveCtx } from "@burojs/file-storage";
@@ -72,6 +73,75 @@ interface LiveCoordinator {
72
73
  dispose(): Promise<void>;
73
74
  }
74
75
  //#endregion
76
+ //#region src/data/live/query-live-observer.d.ts
77
+ interface QueryLiveObserverState {
78
+ readonly live: LiveState;
79
+ /** Only observer-detected protocol violations synthesize query failure. */
80
+ readonly protocolError?: DataError;
81
+ }
82
+ /**
83
+ * Reduces a descriptor's shared live lease into its canonical TanStack cache
84
+ * entry. It owns no provider resources beyond its lease and becomes inert as
85
+ * soon as that lease is released, which prevents retired session work from
86
+ * writing into a replacement cache partition.
87
+ */
88
+ declare class QueryLiveObserver<T extends ErasedQueryDescriptor = ErasedQueryDescriptor> {
89
+ #private;
90
+ readonly identity: string;
91
+ constructor(args: {
92
+ readonly descriptor: T;
93
+ readonly runtime: DataRuntimeContextValue;
94
+ readonly queryClient: QueryClient;
95
+ });
96
+ getState: () => QueryLiveObserverState;
97
+ subscribe: (listener: () => void) => (() => void);
98
+ /** Whether a regular query should execute after the selected live plan is ready. */
99
+ get queryEnabled(): boolean;
100
+ execute(signal: AbortSignal): Promise<QueryResultFor<T>>;
101
+ dispose(): void;
102
+ }
103
+ //#endregion
104
+ //#region src/data/live/live-queries.d.ts
105
+ /** One consumer's hold on the shared observer of a descriptor. */
106
+ interface LiveQueryRetention {
107
+ readonly observer: QueryLiveObserver;
108
+ /** Drops this hold. Idempotent; the last release disposes the observer. */
109
+ release(): void;
110
+ }
111
+ /**
112
+ * The runtime's one `QueryLiveObserver` per descriptor identity and session
113
+ * generation, shared by every mounted consumer of that descriptor and kept
114
+ * alive while any of them retains it. Invariant: `queryFn` resolves the
115
+ * observer when the request runs, never when the options were built, because
116
+ * TanStack runs the `queryFn` of whichever consumer rendered last.
117
+ */
118
+ interface LiveQueryRegistry {
119
+ /**
120
+ * Retains the shared observer for `descriptor` at the current session
121
+ * generation, creating it (and acquiring its live lease) on the first hold.
122
+ * Call it from a committed effect, and only for an enabled query.
123
+ */
124
+ retain(descriptor: ErasedQueryDescriptor): LiveQueryRetention;
125
+ /**
126
+ * The TanStack `queryFn` for `descriptor`. It executes through the retained
127
+ * observer of the current generation when a consumer holds one, and falls
128
+ * back to a plain `executeQuery` when none does (a disabled query's manual
129
+ * `refetch()`, for instance).
130
+ *
131
+ * During a session-generation change there is a short window between the
132
+ * old observer retiring and the consumer's effect retaining the new one; a
133
+ * request started there also takes the fallback. That is harmless: the
134
+ * transition's own `cancelQueries()` cancels it, or its `clear()` has
135
+ * already detached the query it would write to, and the re-retained
136
+ * observer runs the next generation's first fetch itself.
137
+ */
138
+ queryFn(descriptor: ErasedQueryDescriptor): (context: {
139
+ readonly signal: AbortSignal;
140
+ }) => Promise<unknown>;
141
+ /** Number of live entries, for diagnostics and tests. */
142
+ readonly size: number;
143
+ }
144
+ //#endregion
75
145
  //#region src/data/provider-registry.d.ts
76
146
  interface ProviderServiceRef {
77
147
  readonly id: string;
@@ -96,6 +166,13 @@ interface DataRuntimeContextValue {
96
166
  readonly providers: DataProviderRegistry;
97
167
  readonly batchCoordinator: BatchCoordinator;
98
168
  readonly liveCoordinator: LiveCoordinator;
169
+ /**
170
+ * One shared live query observer per descriptor and session generation,
171
+ * retained by the query hooks.
172
+ *
173
+ * @internal
174
+ */
175
+ readonly liveQueries: LiveQueryRegistry;
99
176
  readonly liveSetupTimeoutMs: number;
100
177
  /** Present only in React trees configured with a native AuthProvider. */
101
178
  readonly authController?: AuthController;
@@ -215,6 +292,10 @@ declare function useIsAuthenticated(): UseQueryResult<AuthCheckResult, DataError
215
292
  declare function useLogin(): UseMutationResult<AuthResult, DataError, unknown>;
216
293
  declare function useLogout(): UseMutationResult<AuthResult, DataError, unknown>;
217
294
  //#endregion
295
+ //#region src/auth/context.d.ts
296
+ /** The auth controller, or `null` when `BuroProvider` has no `authProvider`. Never throws. */
297
+ declare function useOptionalAuthController(): AuthController | null;
298
+ //#endregion
218
299
  //#region src/hooks/data/use-query-descriptor.d.ts
219
300
  type NativeQueryOptions<TData, TSelected = TData> = Omit<UseQueryOptions<TData, DataError, TSelected, DataQueryKey>, 'queryKey' | 'queryFn' | 'placeholderData' | 'refetchInterval'> & {
220
301
  /**
@@ -259,6 +340,14 @@ type UseOneOptions<R extends AnyResource$6, TSelected = QueryResultFor<OneDescri
259
340
  declare function useOne<R extends AnyResource$6, TSelected = QueryResultFor<OneDescriptor<R>>>(resource: R, options: UseOneOptions<R, TSelected>): NativeQueryResult<QueryResultFor<OneDescriptor<R>>, TSelected>;
260
341
  declare function useOne<TSelected = QueryResultFor<OneDescriptor<ErasedResourceHandle>>>(resource: string, options: UseOneOptions<ErasedResourceHandle, TSelected>): NativeQueryResult<QueryResultFor<OneDescriptor<ErasedResourceHandle>>, TSelected>;
261
342
  //#endregion
343
+ //#region src/hooks/data/retry-record-read.d.ts
344
+ /**
345
+ * Record reads retry only what the provider called retryable, at most twice.
346
+ * TanStack's default — three retries, about seven seconds before an error
347
+ * shows — would also retry a `not-found` or a `forbidden` that cannot succeed.
348
+ */
349
+ declare function retryRecordRead(failureCount: number, error: DataError): boolean;
350
+ //#endregion
262
351
  //#region src/hooks/data/use-many.d.ts
263
352
  type AnyResource$5 = ResourceHandle<unknown, Record<string, FieldDefBase>, string, string>;
264
353
  type UseManyOptions<R extends AnyResource$5, TSelected = QueryResultFor<ManyDescriptor<R>>> = {
@@ -369,23 +458,12 @@ type UseCreateOneOptions<R extends AnyResource$2> = NativeMutationOptions<Mutati
369
458
  }> & {
370
459
  readonly providerOptionsOverride?: PortableObject;
371
460
  };
372
- type UseCreateManyOptions<R extends AnyResource$2> = NativeMutationOptions<MutationResultFor<CreateManyDescriptor<R>>, {
373
- readonly values: readonly InferCreateInput<R>[];
374
- }> & {
375
- readonly providerOptionsOverride?: PortableObject;
376
- };
377
461
  declare function useCreateOne<R extends AnyResource$2>(resource: R, options?: UseCreateOneOptions<R>): UseMutationResult<MutationResultFor<CreateOneDescriptor<R>>, DataError, {
378
462
  readonly values: InferCreateInput<R>;
379
463
  }>;
380
464
  declare function useCreateOne(resource: string, options?: UseCreateOneOptions<ErasedResourceHandle>): UseMutationResult<MutationResultFor<CreateOneDescriptor<ErasedResourceHandle>>, DataError, {
381
465
  readonly values: unknown;
382
466
  }>;
383
- declare function useCreateMany<R extends AnyResource$2>(resource: R, options?: UseCreateManyOptions<R>): UseMutationResult<MutationResultFor<CreateManyDescriptor<R>>, DataError, {
384
- readonly values: readonly InferCreateInput<R>[];
385
- }>;
386
- declare function useCreateMany(resource: string, options?: UseCreateManyOptions<ErasedResourceHandle>): UseMutationResult<MutationResultFor<CreateManyDescriptor<ErasedResourceHandle>>, DataError, {
387
- readonly values: readonly unknown[];
388
- }>;
389
467
  //#endregion
390
468
  //#region src/hooks/data/use-update.d.ts
391
469
  type AnyResource$1 = ResourceHandle<any, Record<string, FieldDefBase>, string, string>;
@@ -434,6 +512,258 @@ type UseCustomMutationOptions<TInput, TOutput> = NativeMutationOptions<TOutput,
434
512
  /** Resolves a trusted custom mutation and its declaration-owned effects. */
435
513
  declare function useCustomMutation<THandle extends CustomMutationHandle<unknown, unknown>>(handle: THandle, options?: UseCustomMutationOptions<InferCustomInput<THandle>, InferCustomOutput<THandle>>): UseMutationResult<InferCustomOutput<THandle>, DataError, InferCustomInput<THandle>>;
436
514
  //#endregion
515
+ //#region src/frames/view-props.d.ts
516
+ /** Reads `frames.<id>.<key>` (the block convention, foundation 12). */
517
+ type FrameTranslate = (key: string, options?: {
518
+ readonly params?: Record<string, unknown>;
519
+ readonly count?: number;
520
+ }) => string;
521
+ /**
522
+ * The attributes of the styling contract a node frame spreads on its root: `data-buro-node`,
523
+ * `data-buro-key`, `data-buro-tone`, `data-buro-state` (spec 3.5).
524
+ */
525
+ type FrameNodeAttributes = Readonly<Record<`data-buro-${string}`, string>>;
526
+ /** The slots a record frame view receives: `body` always rendered, every other slot or `null`. */
527
+ type RecordFrameSlots<S extends SlotMap> = {
528
+ readonly body: ReactElement;
529
+ } & { readonly [K in Exclude<keyof S & string, 'body'>]: ReactElement | null; };
530
+ /** What a record frame's view is rendered with (spec 3.3). */
531
+ interface RecordFrameViewProps<F extends AnyFrameHandle = FrameHandle<'record'>> {
532
+ /**
533
+ * Each slot's planned tree inside its framework wrapper; `null` when the slot is optional and
534
+ * omitted, absent in this mode (create) or planned empty. `body` holds the form in edit and
535
+ * create.
536
+ */
537
+ readonly slots: RecordFrameSlots<F['slots']>;
538
+ readonly host: 'page' | 'sheet' | 'dialog';
539
+ readonly mode: 'show' | 'edit' | 'create';
540
+ readonly screen: {
541
+ readonly resource: string;
542
+ readonly entry: 'route' | 'quick';
543
+ };
544
+ /** The screen's record as the user may see it; `undefined` before it is read and in create. */
545
+ readonly record: Readonly<Record<string, unknown>> | undefined;
546
+ readonly t: FrameTranslate;
547
+ }
548
+ /** What a peek frame's view is rendered with (spec 3.9). */
549
+ interface PeekFrameViewProps<F extends AnyFrameHandle = FrameHandle<'peek'>> {
550
+ readonly slots: { readonly [K in keyof F['slots'] & string]: ReactElement | null; };
551
+ /** The one row the peek read, filtered as the peek shows it. */
552
+ readonly record: Readonly<Record<string, unknown>>;
553
+ /** Opens the record's `quick.show` through the entry gate (the title click). */
554
+ open(): void;
555
+ readonly t: FrameTranslate;
556
+ }
557
+ /**
558
+ * What a list frame's view is rendered with (P7-34): the planned trees of its slots (`null` when a
559
+ * slot is omitted or planned empty) and the resource's registry name. The rows are not a prop: a
560
+ * frame arranges the framework's pieces (`<ListControls/>`, `<ListBody/>`, …), which read the list
561
+ * controller the framework owns outside the frame.
562
+ */
563
+ interface ListFrameViewProps<F extends AnyFrameHandle = FrameHandle<'list'>> {
564
+ readonly slots: { readonly [K in keyof F['slots'] & string]: ReactElement | null; };
565
+ readonly resource: string;
566
+ readonly t: FrameTranslate;
567
+ }
568
+ /**
569
+ * What a page frame's view is rendered with (P7 3.14): the page's slots as rendered elements
570
+ * (`body` always), the page label and its parsed `route` params. The framework owns the page's
571
+ * `<h1>`, its access gate and `NavChrome`; the view arranges the slots inside the page body and
572
+ * renders no heading of its own.
573
+ */
574
+ interface PageFrameViewProps<F extends AnyFrameHandle = FrameHandle<'page'>> {
575
+ readonly slots: { readonly [K in keyof F['slots'] & string]: ReactElement | null; };
576
+ /** The page's label, resolved (the framework draws it as the `<h1>`). */
577
+ readonly label: string;
578
+ /** The page's parsed params: the same values `l.ref('route', …)` reads. */
579
+ readonly params: Readonly<Record<string, unknown>>;
580
+ readonly t: FrameTranslate;
581
+ }
582
+ /** What a panel frame's view is rendered with (spec 3.5). */
583
+ interface PanelFrameViewProps {
584
+ /**
585
+ * The panel's content inside the framework's body element; `null` when closed. While pending,
586
+ * the framework's skeleton.
587
+ */
588
+ readonly body: ReactElement | null;
589
+ readonly title?: string;
590
+ readonly description?: string;
591
+ readonly tone?: PanelTone;
592
+ readonly icon?: string;
593
+ readonly collapsible: boolean;
594
+ readonly open: boolean;
595
+ /** Opens or closes a collapsible panel; the engine calls the same function to reveal an error. */
596
+ onToggle(next: boolean): void;
597
+ readonly errorCount: number;
598
+ readonly mode: ScreenMode;
599
+ /** The id to put on the title element: the body is labelled by it. */
600
+ readonly titleId: string;
601
+ /** The id of the framework's body element, for the toggle's `aria-controls`. */
602
+ readonly bodyId: string;
603
+ /** The node's «i», or `null`. */
604
+ readonly info: ReactElement | null;
605
+ readonly nodeAttributes: FrameNodeAttributes;
606
+ readonly t: FrameTranslate;
607
+ }
608
+ /** One tab of a tabs frame. */
609
+ interface FrameTab {
610
+ readonly key: string;
611
+ readonly title: string;
612
+ readonly errorCount: number;
613
+ /** Only the active tab's panel is rendered (demand is the planner's). */
614
+ readonly panel: ReactElement | null;
615
+ }
616
+ /** What a tabs frame's view is rendered with (spec 3.5). */
617
+ interface TabsFrameViewProps {
618
+ readonly tabs: readonly FrameTab[];
619
+ readonly active: string;
620
+ onSelect(key: string): void;
621
+ readonly info: ReactElement | null;
622
+ readonly nodeAttributes: FrameNodeAttributes;
623
+ readonly t: FrameTranslate;
624
+ }
625
+ /** One step of a steps frame. */
626
+ interface FrameStep {
627
+ readonly key: string;
628
+ readonly title: string;
629
+ readonly errorCount: number;
630
+ readonly state: 'current' | 'complete' | 'reachable' | 'locked';
631
+ /**
632
+ * Outside a wizard, every step's whole panel; inside, the current step's body only (the frame
633
+ * draws its heading), `null` for the others.
634
+ */
635
+ readonly panel: ReactElement | null;
636
+ readonly description?: string;
637
+ /** The id to put on the step's heading: the step's body is labelled by it. */
638
+ readonly titleId: string;
639
+ /** The step panel's «i», or `null`. */
640
+ readonly info: ReactElement | null;
641
+ /** The step panel's styling attributes (`data-buro-node="panel"`, its key, tone and state). */
642
+ readonly nodeAttributes: FrameNodeAttributes;
643
+ }
644
+ /** What a steps frame's view is rendered with (spec 3.5). */
645
+ interface StepsFrameViewProps {
646
+ /** A create whose root is this steps node: one step at a time. */
647
+ readonly wizard: boolean;
648
+ readonly steps: readonly FrameStep[];
649
+ readonly current: string;
650
+ goTo(key: string): void;
651
+ back(): void;
652
+ /** Validates the current step, then moves on. */
653
+ next(): void;
654
+ /** Attach to the current step's heading: the framework focuses it when the step changes. */
655
+ readonly headingRef: RefObject<HTMLElement | null>;
656
+ readonly info: ReactElement | null;
657
+ readonly nodeAttributes: FrameNodeAttributes;
658
+ readonly t: FrameTranslate;
659
+ }
660
+ /** The props a frame's view receives, by the frame's kind. */
661
+ type FrameViewProps<F extends AnyFrameHandle = AnyFrameHandle> = F['for'] extends 'record' ? RecordFrameViewProps<F> : F['for'] extends 'peek' ? PeekFrameViewProps<F> : F['for'] extends 'list' ? ListFrameViewProps<F> : F['for'] extends 'page' ? PageFrameViewProps<F> : F['for'] extends 'panel' ? PanelFrameViewProps : F['for'] extends 'tabs' ? TabsFrameViewProps : F['for'] extends 'steps' ? StepsFrameViewProps : never;
662
+ //#endregion
663
+ //#region src/frames/implementation.d.ts
664
+ /**
665
+ * A frame's view, registered in `<BuroProvider frameImplementations>` (spec 3.2). Without a type
666
+ * argument, the view of any frame.
667
+ */
668
+ interface FrameImplementation<F extends AnyFrameHandle = any> {
669
+ /** `<namespace>:<name>`. */
670
+ readonly id: string;
671
+ readonly frame: EntityRef;
672
+ readonly for: FrameKind;
673
+ /** The frame handle `implementFrame` was given. */
674
+ readonly definition: F;
675
+ readonly component?: ComponentType<FrameViewProps<F>>;
676
+ readonly load?: () => Promise<{
677
+ readonly default: ComponentType<FrameViewProps<F>>;
678
+ }>;
679
+ }
680
+ /** The app's frame views, keyed by frame id. */
681
+ type FrameImplementationCatalog = Readonly<Record<string, FrameImplementation<any>>>;
682
+ /**
683
+ * The view of a frame: `component`, or `load` (a module whose default export is the view,
684
+ * requested when a screen first renders it). Register the result through
685
+ * `composeFrameImplementations` on `<BuroProvider frameImplementations>`.
686
+ *
687
+ * @throws TypeError when `frame` is not a frame handle, or when both or neither of `load` and
688
+ * `component` are given.
689
+ */
690
+ declare function implementFrame<F extends AnyFrameHandle>(frame: F, impl: {
691
+ readonly component: ComponentType<FrameViewProps<F>>;
692
+ readonly load?: never;
693
+ } | {
694
+ readonly load: NonNullable<FrameImplementation<F>['load']>;
695
+ readonly component?: never;
696
+ }): FrameImplementation<F>;
697
+ /**
698
+ * The app's frame views as one catalogue, keyed by frame id: takes `implementFrame` results.
699
+ * A duplicate id is a development error and the later entry wins; an entry for a built-in
700
+ * (`buro:`) frame is a development error and is dropped — the app's default frame is chosen in
701
+ * `defineAppDefinition({ frames })`, never by replacing a built-in view (P6-9).
702
+ *
703
+ * @throws TypeError for an argument that is not an `implementFrame` result.
704
+ */
705
+ declare function composeFrameImplementations(...entries: readonly FrameImplementation<any>[]): FrameImplementationCatalog;
706
+ //#endregion
707
+ //#region src/blocks/implementation.d.ts
708
+ /** Whether a prop of `B` is a `block()` prop, by its schema's output. */
709
+ type IsBlockProp<O> = O extends {
710
+ readonly block: EntityRef;
711
+ } ? true : false;
712
+ /**
713
+ * The props a block view receives: each prop's schema output, a `block()` prop as the rendered
714
+ * nested placement (`ReactElement`, `ReactElement[]` for a list).
715
+ */
716
+ type BlockViewPropValues<B> = { [K in keyof InferBlockProps<B>]: InferBlockProps<B>[K] extends readonly (infer E)[] ? IsBlockProp<E> extends true ? readonly ReactElement[] : InferBlockProps<B>[K] : IsBlockProp<InferBlockProps<B>[K]> extends true ? ReactElement : InferBlockProps<B>[K]; };
717
+ /** The names of a block's `data` entries. */
718
+ type BlockDataEntryName<B> = keyof InferBlockData<B> & string;
719
+ /** What a block view is rendered with (spec 8.2). */
720
+ interface BlockViewProps<B> {
721
+ /** Each prop's schema output; a `block()` prop is the nested placement, rendered. */
722
+ readonly props: BlockViewPropValues<B>;
723
+ /** Ready values only: the framework chrome draws pending, failed and absent. */
724
+ readonly data: Readonly<Record<BlockDataEntryName<B>, unknown>>;
725
+ /** Reads `blocks.<id>.<key>`. */
726
+ readonly t: (key: string, options?: {
727
+ readonly params?: Record<string, unknown>;
728
+ readonly count?: number;
729
+ }) => string;
730
+ }
731
+ /** A block's view, registered in `<BuroProvider blockImplementations>` (spec 8.2). */
732
+ interface BlockImplementation<B = unknown> {
733
+ /** `<namespace>:<name>`. */
734
+ readonly id: string;
735
+ readonly block: EntityRef;
736
+ /** The block handle `implementBlock` was given (`renderBlock` validates props through it). */
737
+ readonly definition: B;
738
+ readonly load?: () => Promise<{
739
+ readonly default: ComponentType<BlockViewProps<B>>;
740
+ }>;
741
+ readonly component?: ComponentType<BlockViewProps<B>>;
742
+ }
743
+ type BlockImplementationCatalog = Readonly<Record<string, BlockImplementation<any>>>;
744
+ /**
745
+ * The view of a block: `load` (a module whose default export is the view, requested only when a
746
+ * placement mounts) or `component`. Register the result through `composeBlockImplementations`
747
+ * on `<BuroProvider blockImplementations>`.
748
+ *
749
+ * @throws TypeError when `block` is not a block handle, or when both or neither of `load` and
750
+ * `component` are given.
751
+ */
752
+ declare function implementBlock<B>(block: B, impl: {
753
+ readonly load: NonNullable<BlockImplementation<B>['load']>;
754
+ readonly component?: never;
755
+ } | {
756
+ readonly component: ComponentType<BlockViewProps<B>>;
757
+ readonly load?: never;
758
+ }): BlockImplementation<B>;
759
+ /**
760
+ * The app's block views as one catalogue, keyed by block id: takes `implementBlock` results (not
761
+ * records). A duplicate id is a development error and the later entry wins.
762
+ *
763
+ * @throws TypeError for an argument that is not an `implementBlock` result.
764
+ */
765
+ declare function composeBlockImplementations(...entries: readonly BlockImplementation<any>[]): BlockImplementationCatalog;
766
+ //#endregion
437
767
  //#region src/fields/types.d.ts
438
768
  /** The render surface requested by a field consumer. */
439
769
  type FieldRenderSurface = 'input' | 'display';
@@ -475,7 +805,8 @@ interface FieldTestIds {
475
805
  * domain format.
476
806
  */
477
807
  interface FieldImplementationLayout {
478
- readonly fullWidth?: boolean;
808
+ /** The default width of a field this implementation renders (`ui.size` wins). */
809
+ readonly size?: LayoutSize;
479
810
  }
480
811
  /** Locale-aware, renderer-safe access to the active application content model. */
481
812
  interface FieldI18n extends I18nCaps {
@@ -498,7 +829,12 @@ interface FieldRenderContext<TMeta extends Readonly<Record<string, unknown>> = R
498
829
  readonly ui: FieldUiHints;
499
830
  readonly options: readonly FieldOption[];
500
831
  readonly i18n: FieldI18n;
501
- readonly frames: FieldContextFrames;
832
+ /**
833
+ * What a portable condition reads against: the record, the values and the
834
+ * app scope. On an input surface `values` are the live form values; on a
835
+ * display surface there is no form, so `values` is the record itself.
836
+ */
837
+ readonly sources: FieldContextSources;
502
838
  readonly pending: boolean;
503
839
  readonly touched: boolean;
504
840
  readonly issues: ValidationIssues;
@@ -536,6 +872,13 @@ interface FieldImplementation<T, TMeta extends Readonly<Record<string, unknown>>
536
872
  readonly CollectionInput?: ComponentType<CollectionInputProps<T, TMeta>>;
537
873
  readonly CollectionDisplay?: ComponentType<CollectionDisplayProps<T, TMeta>>;
538
874
  readonly layout?: FieldImplementationLayout;
875
+ /**
876
+ * The schema of the `meta` this implementation reads (P7 3.3): applied to the resolved `meta`
877
+ * (refs read) at render, so `props.meta` is the schema's output. A refusal renders the field
878
+ * misconfigured and blocks submit; `checkApp({ fieldImplementations })` checks literal metas
879
+ * ahead of time. Without one, the implementation receives the resolved record untyped.
880
+ */
881
+ readonly meta?: StandardSchemaV1<unknown, TMeta>;
539
882
  }
540
883
  type FieldImplementationComponent = ComponentType<ScalarInputProps<unknown>> | ComponentType<ScalarDisplayProps<unknown>> | ComponentType<CollectionInputProps<unknown>> | ComponentType<CollectionDisplayProps<unknown>>;
541
884
  type FieldImplementationSource = 'explicit' | 'format' | 'collection-format' | 'array-shell' | 'base' | 'fallback';
@@ -560,6 +903,8 @@ interface FieldImplementationDiagnostic {
560
903
  }
561
904
  interface ResolvedFieldImplementation {
562
905
  readonly Component?: FieldImplementationComponent;
906
+ /** The catalogue entry the field resolved to (its `meta` schema, P7 3.3); absent on a fallback. */
907
+ readonly implementation?: FieldImplementation<any, any>;
563
908
  readonly multiple: boolean;
564
909
  readonly source: FieldImplementationSource;
565
910
  readonly layout: FieldImplementationLayout;
@@ -693,30 +1038,57 @@ declare function UIInstanceProvider({ instance, presentation, children }: {
693
1038
  readonly children: ReactNode;
694
1039
  }): import("react/jsx-runtime").JSX.Element;
695
1040
  //#endregion
696
- //#region src/ui/use-ui-present.d.ts
697
- /**
698
- * Reads the current activation: the surface from the nearest
699
- * `UIActivationSurfaceProvider` (defaulting to `{ type: 'global' }` when
700
- * there isn't one) plus a live snapshot of the app's resolver slices, if a
701
- * `BuroProvider` is mounted above. Outside one, `slices` is `{}` — this
702
- * hook (and `useUIPresent`, which is built on it) works standalone, e.g. in
703
- * a unit test that only wires up `UIPresentationRuntimeProvider`.
704
- */
705
- declare function useCurrentUIActivation(): UIActivationContext;
706
- /** An imperative present request; `activation` is captured automatically and need not (usually) be supplied. */
707
- type UIPresentInput = Omit<UIPresentRequest, 'activation'> & {
708
- readonly activation?: Partial<UIActivationContext>;
709
- };
710
- type UIPresent = (request: UIPresentInput) => UIPresentResult;
711
- /**
712
- * Returns a `present` function that captures the current activation
713
- * (surface + resolver slices) at call time and forwards the request to the
714
- * `UIPresentationRuntime` supplied by the nearest `UIPresentationRuntimeProvider`.
715
- * A caller may override `surface` and/or `slices` explicitly via the
716
- * `activation` field of the request; anything left unset falls back to the
717
- * captured activation.
718
- */
719
- declare function useUIPresent(): UIPresent;
1041
+ //#region src/quick/quick-stack.d.ts
1042
+ /** One entry of a quick overlay: a record's quick screen in a mode. */
1043
+ interface QuickEntry {
1044
+ readonly resource: string;
1045
+ readonly mode: 'show' | 'edit' | 'create';
1046
+ /** The record of `show` and `edit`; absent on `create`. */
1047
+ readonly recordId?: string;
1048
+ /** `create`'s trusted defaults (the caller's tier, P1b+P1c decision 7). */
1049
+ readonly defaults?: Readonly<Record<string, unknown>>;
1050
+ /**
1051
+ * `create`'s user input (a search text typed into a relation select): form values the user can
1052
+ * see and change, applied once to the fields the plan holds `active`, never a trusted default
1053
+ * (P6 plan ruling 7).
1054
+ */
1055
+ readonly input?: Readonly<Record<string, unknown>>;
1056
+ /** The entry's record title once its screen has read it: what "Back to …" names. */
1057
+ readonly title?: string;
1058
+ }
1059
+ /** A created row, or `null`: what `useQuick().create` resolves with. */
1060
+ type QuickSettledRow = Readonly<Record<string, unknown>> | null;
1061
+ /**
1062
+ * The history of one quick overlay (spec 4.9): a stack of entries of which only the top is
1063
+ * mounted. A relation inside a sheet pushes; Back pops; Edit in `quick.show` replaces the top.
1064
+ * The history is never in the URL. An external store: `subscribe` / `getSnapshot` for
1065
+ * `useSyncExternalStore`, and `getSnapshot()` changes identity on every change.
1066
+ *
1067
+ * `settled` resolves once, with the row a `quick.create` saved or `null` when the overlay closed
1068
+ * without one.
1069
+ */
1070
+ declare class QuickStack {
1071
+ private stack;
1072
+ private readonly listeners;
1073
+ private resolveSettled;
1074
+ private isSettled;
1075
+ readonly settled: Promise<QuickSettledRow>;
1076
+ constructor(first: QuickEntry);
1077
+ get entries(): readonly QuickEntry[];
1078
+ get top(): QuickEntry;
1079
+ push(entry: QuickEntry): void;
1080
+ /** Back: drops the top entry. A stack of one is never emptied (closing is the overlay's). */
1081
+ pop(): void;
1082
+ /** Switches the top entry in place (Edit ↔ show), keeping the depth. */
1083
+ replaceTop(entry: QuickEntry): void;
1084
+ /** Records the top entry's title once its record is read. */
1085
+ nameTop(title: string): void;
1086
+ /** Resolve `settled`, once: later calls change nothing. */
1087
+ settle(row: QuickSettledRow): void;
1088
+ subscribe: (listener: () => void) => (() => void);
1089
+ getSnapshot: () => readonly QuickEntry[];
1090
+ private publish;
1091
+ }
720
1092
  //#endregion
721
1093
  //#region src/ui/ui-presentations.d.ts
722
1094
  /**
@@ -731,6 +1103,93 @@ interface UIPresentationHostProps {
731
1103
  readonly component: ComponentType<Record<string, unknown>>;
732
1104
  readonly props: Readonly<Record<string, unknown>>;
733
1105
  readonly options: Readonly<Record<string, unknown>>;
1106
+ /** A popover's anchor, passed through by reference (`host: 'popover'` only). */
1107
+ readonly anchor?: UIPresentAnchor;
1108
+ }
1109
+ /** The reserved component key of a quick entry: resolved from `presentationHosts.screens.quick`. */
1110
+ declare const QUICK_COMPONENT = "buro:quick";
1111
+ /** The reserved component key of a peek: resolved from `presentationHosts.screens.peek`. */
1112
+ declare const PEEK_COMPONENT = "buro:peek";
1113
+ /** The reserved component key of a flow's host: resolved from `presentationHosts.screens.flow`. */
1114
+ declare const FLOW_COMPONENT = "buro:flow";
1115
+ /** The reserved component key of a modal action's block: resolved from `presentationHosts.screens.block`. */
1116
+ declare const BLOCK_COMPONENT = "buro:block";
1117
+ /**
1118
+ * What the block screen receives (P7 3.16): the block a modal action presents and its portable
1119
+ * props. For an item action the clicked row and its resource ride along: the screen reads the row
1120
+ * projected by field view, binds `ref('record', …)` from it, and draws nothing until that is decided.
1121
+ */
1122
+ interface BlockScreenProps {
1123
+ readonly block: {
1124
+ readonly namespace: string;
1125
+ readonly id: string;
1126
+ };
1127
+ readonly props: Readonly<Record<string, unknown>>;
1128
+ readonly resource?: string;
1129
+ readonly record?: Row;
1130
+ readonly label?: string;
1131
+ }
1132
+ /** What the quick screen receives: the overlay's history, whose top entry it renders. */
1133
+ interface QuickScreenProps {
1134
+ readonly stack: QuickStack;
1135
+ }
1136
+ /** What the peek screen receives: the target and the key it previews. */
1137
+ interface PeekScreenProps {
1138
+ readonly target: string;
1139
+ readonly id?: string;
1140
+ readonly by?: {
1141
+ readonly field: string;
1142
+ readonly key: string;
1143
+ };
1144
+ }
1145
+ /** A flow the host starts itself (P4-C review I1): what to run, on what. */
1146
+ interface FlowStart {
1147
+ readonly target: FlowRef | FlowTarget;
1148
+ readonly record?: Row;
1149
+ readonly args?: Row;
1150
+ readonly selection?: {
1151
+ readonly ids: readonly string[];
1152
+ readonly count: number;
1153
+ };
1154
+ }
1155
+ /**
1156
+ * What a flow's host screen receives (P4 spec 4.15). Either a run its opener holds (`run` and
1157
+ * `compiled`: a `{ label, submit }` flow's outcome, drawn in a dialog), or a flow to start
1158
+ * (`start`): the host then owns the run, so the host outlives its opener — a row that left the
1159
+ * page, a menu item that closed — and its form, its draft and a pending write with it. `then`
1160
+ * runs through the opener's dispatcher scope (`effects`) after the host closed; `onSettle`
1161
+ * reports each outcome to the opener.
1162
+ */
1163
+ interface FlowScreenProps {
1164
+ readonly run?: FlowRun;
1165
+ readonly start?: FlowStart;
1166
+ /** `'page'` and `'inline'` are the screen drawn in a route or a show tree (P6), not presented. */
1167
+ readonly host: 'dialog' | 'sheet' | 'page' | 'inline';
1168
+ readonly label: string;
1169
+ readonly title?: string;
1170
+ readonly compiled?: CompiledFlow;
1171
+ readonly record?: Row;
1172
+ readonly args?: Row;
1173
+ readonly selection?: {
1174
+ readonly ids: readonly string[];
1175
+ readonly count: number;
1176
+ };
1177
+ /** The effects of the dispatcher scope the flow was started in (4.13, I12). */
1178
+ readonly effects?: () => DispatcherEffects;
1179
+ readonly onSettle?: (result: FlowResult) => void;
1180
+ }
1181
+ /**
1182
+ * The UI kit's overlay screens (P3-6): the framework presents them under the reserved keys
1183
+ * `buro:quick`, `buro:peek` and `buro:flow` and resolves those keys from here only, never from
1184
+ * the app's `components`.
1185
+ */
1186
+ interface UIPresentationScreens {
1187
+ readonly quick?: ComponentType<QuickScreenProps>;
1188
+ readonly peek?: ComponentType<PeekScreenProps>;
1189
+ /** A flow's host screen (P4 spec 4.15), in a dialog or a sheet. */
1190
+ readonly flow?: ComponentType<FlowScreenProps>;
1191
+ /** A modal action's block (P7 3.16), in a dialog or a sheet. */
1192
+ readonly block?: ComponentType<BlockScreenProps>;
734
1193
  }
735
1194
  /**
736
1195
  * What a host declares it understands about its `options` record: a flat
@@ -763,7 +1222,10 @@ type UIPresentationHost = ComponentType<UIPresentationHostProps> & {
763
1222
  * simply renders nothing (no crash, no console noise): that is the expected
764
1223
  * shape for an app that has not wired concrete hosts (yet).
765
1224
  */
766
- type UIPresentationHosts = Readonly<Partial<Record<UIPresentationDescriptor['host'], UIPresentationHost>>>;
1225
+ type UIPresentationHosts = Readonly<Partial<Record<UIPresentHost, UIPresentationHost>>> & {
1226
+ /** The UI kit's overlay screens (`shadcnPresentationHosts.screens`). */
1227
+ readonly screens?: UIPresentationScreens;
1228
+ };
767
1229
  interface UIPresentationsProps {
768
1230
  readonly hosts: UIPresentationHosts;
769
1231
  }
@@ -783,6 +1245,31 @@ interface UIPresentationsProps {
783
1245
  */
784
1246
  declare function UIPresentations({ hosts }: UIPresentationsProps): ReactElement;
785
1247
  //#endregion
1248
+ //#region src/ui/use-ui-present.d.ts
1249
+ /**
1250
+ * Reads the current activation: the surface from the nearest
1251
+ * `UIActivationSurfaceProvider` (defaulting to `{ type: 'global' }` when
1252
+ * there isn't one) plus a live snapshot of the app's resolver slices, if a
1253
+ * `BuroProvider` is mounted above. Outside one, `slices` is `{}` — this
1254
+ * hook (and `useUIPresent`, which is built on it) works standalone, e.g. in
1255
+ * a unit test that only wires up `UIPresentationRuntimeProvider`.
1256
+ */
1257
+ declare function useCurrentUIActivation(): UIActivationContext;
1258
+ /** An imperative present request; `activation` is captured automatically and need not (usually) be supplied. */
1259
+ type UIPresentInput = Omit<UIPresentRequest, 'activation'> & {
1260
+ readonly activation?: Partial<UIActivationContext>;
1261
+ };
1262
+ type UIPresent = (request: UIPresentInput) => UIPresentResult;
1263
+ /**
1264
+ * Returns a `present` function that captures the current activation
1265
+ * (surface + resolver slices) at call time and forwards the request to the
1266
+ * `UIPresentationRuntime` supplied by the nearest `UIPresentationRuntimeProvider`.
1267
+ * A caller may override `surface` and/or `slices` explicitly via the
1268
+ * `activation` field of the request; anything left unset falls back to the
1269
+ * captured activation.
1270
+ */
1271
+ declare function useUIPresent(): UIPresent;
1272
+ //#endregion
786
1273
  //#region src/ui/mount.d.ts
787
1274
  /**
788
1275
  * Who renders the presentation stack.
@@ -790,8 +1277,10 @@ declare function UIPresentations({ hosts }: UIPresentationsProps): ReactElement;
790
1277
  * `'framework'` (default) — `BuroProvider` mounts it beside `{children}`,
791
1278
  * inside every framework provider. Presented components reach field
792
1279
  * implementations, i18n, UI settings and the doc panel, but NOT providers the
793
- * app mounts inside `{children}` (dispatch, relation aggregator, link
794
- * component): those render strictly below the framework's own mount.
1280
+ * app mounts inside `{children}` (the link component): those render strictly
1281
+ * below the framework's own mount. The dispatcher, the navigation guard and the
1282
+ * relation aggregator are mounted by `BuroProvider` around both, so they are
1283
+ * reachable in either mode.
795
1284
  *
796
1285
  * `'app'` — the app renders `<BuroPresentationOutlet/>` itself, typically as
797
1286
  * the last child of its own provider stack. Presented components then render
@@ -847,12 +1336,30 @@ interface BuroProviderProps<TSlices extends Record<string, SliceDefinition> = Re
847
1336
  */
848
1337
  resolver: Resolver<TSlices>;
849
1338
  i18n: I18nFacade;
1339
+ /**
1340
+ * The `localStorage` key a language control (`useLanguageSwitch`,
1341
+ * `<LanguageSwitch>`) saves the chosen locale under. Read it back at boot the
1342
+ * same way (`loadLocale(key)`) so the choice survives a reload. Default
1343
+ * `'buro:language'`.
1344
+ */
1345
+ languageStorageKey?: string;
850
1346
  components: Record<string, unknown>;
851
1347
  /**
852
1348
  * Immutable, host-provided React field implementations. They are scoped to
853
1349
  * this provider tree and are intentionally outside AppRuntime/manifests.
854
1350
  */
855
1351
  fieldImplementations?: FieldImplementationCatalog;
1352
+ /**
1353
+ * The app's block views (`composeBlockImplementations(...)`), keyed by block id. The renderer
1354
+ * composes its built-in views first and these over them: an app entry with a built-in id
1355
+ * replaces that view (with a development notice).
1356
+ */
1357
+ blockImplementations?: BlockImplementationCatalog;
1358
+ /**
1359
+ * The app's frame views (`composeFrameImplementations(...)`), keyed by frame id. The kit draws
1360
+ * the built-in (`buro:`) frames itself; an app entry for one is ignored (P6-9).
1361
+ */
1362
+ frameImplementations?: FrameImplementationCatalog;
856
1363
  /**
857
1364
  * Article bodies for the documentation panel — the map of lazy loaders plus
858
1365
  * an optional fallback locale.
@@ -885,8 +1392,10 @@ interface BuroProviderProps<TSlices extends Record<string, SliceDefinition> = Re
885
1392
  * behavior: `BuroProvider` mounts `<UIPresentations/>` beside `{children}`.
886
1393
  * `'app'` hands the mount to the app, which must render
887
1394
  * `<BuroPresentationOutlet/>` inside its own provider stack — the only way a
888
- * presented component can reach app-level providers (dispatch, relation
889
- * aggregator, link component) as the SAME instances the app tree uses.
1395
+ * presented component can reach app-level providers (the link component) as
1396
+ * the SAME instances the app tree uses. The dispatcher, the navigation guard
1397
+ * and the relation aggregator are the framework's own and reach presented
1398
+ * components in both modes.
890
1399
  */
891
1400
  presentations?: PresentationsMode;
892
1401
  /**
@@ -900,90 +1409,66 @@ interface BuroProviderProps<TSlices extends Record<string, SliceDefinition> = Re
900
1409
  * Omitted → no events are emitted and no emission site does any work.
901
1410
  */
902
1411
  audit?: Audit;
1412
+ /**
1413
+ * Overrides for the dispatcher's effects, merged per key over the defaults
1414
+ * `BuroProvider` supplies: `refetch` invalidates the `buro-data` queries,
1415
+ * `notify` reaches the notification sink a UI kit's toaster registers (a
1416
+ * no-op without one), `redirect` navigates through the `RouterAdapter` and is
1417
+ * skipped when it resolves to the current pathname with no query of its own
1418
+ * (the built-in delete fired from a filtered list), `setClipboard` and
1419
+ * `download` use the browser, `modal` presents through the presentation
1420
+ * runtime. `clearSelection` has no default — the caller owns the selection.
1421
+ * Read through a ref, so an inline object neither rebuilds the dispatcher nor
1422
+ * goes stale.
1423
+ */
1424
+ effects?: Partial<DispatcherEffects>;
1425
+ /**
1426
+ * The relation aggregator `<BuroProvider>` mounts around the app and its overlays (when a
1427
+ * `dataRuntime` is given). `maxPage` is the largest page the application's provider serves:
1428
+ * an id batch is read in chunks of this many ids, and a `by` read asks for at most this many
1429
+ * rows. Default 100.
1430
+ */
1431
+ relationBatch?: {
1432
+ readonly maxPage?: number;
1433
+ };
903
1434
  children: ReactNode;
904
1435
  }
905
- declare function BuroProvider<TSlices extends Record<string, SliceDefinition>>({ app, dataRuntime, authProvider, uiSettings, resolver, i18n, components, fieldImplementations, docs, initialManifest, manifestHydrationScope, presentationHosts, presentations, audit, children }: BuroProviderProps<TSlices>): ReactElement;
1436
+ declare function BuroProvider<TSlices extends Record<string, SliceDefinition>>({ app, dataRuntime, authProvider, uiSettings, resolver, i18n, languageStorageKey, components, fieldImplementations, blockImplementations, frameImplementations, docs, initialManifest, manifestHydrationScope, presentationHosts, presentations, audit, effects, relationBatch, children }: BuroProviderProps<TSlices>): ReactElement;
906
1437
  //#endregion
907
1438
  //#region src/audit/context.d.ts
1439
+ /** What an application emits through: its own `app.*` events, and the correlation it shares. */
1440
+ interface AppAudit {
1441
+ /**
1442
+ * Emits an application event. Its `type` must start with `app.`: a framework type
1443
+ * (`form.submit`, `flow.submit`, `nav.enter`) is dropped — the framework emits those itself —
1444
+ * with a development `console.error` naming it.
1445
+ */
1446
+ emit(input: AppAuditEventInput, options?: {
1447
+ readonly initiates?: boolean;
1448
+ }): void;
1449
+ /** The id shared by everything the current user action caused. */
1450
+ currentCorrelationId(): string;
1451
+ readonly sessionId: string;
1452
+ }
908
1453
  /**
909
1454
  * The application's audit emitter, or `null` when `<BuroProvider>` got no
910
1455
  * `audit` prop.
911
1456
  *
912
1457
  * Call sites guard with `audit?.emit(...)`, and that guard is deliberate: it is
913
1458
  * what makes an app without auditing pay nothing, because the work of building
914
- * an event (a form diff, a list snapshot) never starts.
1459
+ * an event (a list snapshot) never starts.
915
1460
  *
916
1461
  * Applications use it to write their own events into the same stream — custom
917
1462
  * pages and hand-written components are otherwise blind spots by construction:
918
1463
  *
919
1464
  * const audit = useAudit();
920
1465
  * audit?.emit({ type: 'app.export.started', payload: { format: 'xlsx' } });
921
- */
922
- declare function useAudit(): Audit | null;
923
- //#endregion
924
- //#region src/audit/form-diff.d.ts
925
- interface BuildFormDiffArgs {
926
- /** Values as they were when the form opened (its baseline). */
927
- readonly before: Readonly<Record<string, unknown>>;
928
- /** Values at submit time. */
929
- readonly after: Readonly<Record<string, unknown>>;
930
- /** The form's field definitions, keyed by field name — the source of `sensitive`. */
931
- readonly fields: Readonly<Record<string, unknown>>;
932
- }
933
- /**
934
- * Build the `diff` of a `form.submit` audit event.
935
- *
936
- * Shared by both form hosts (`useDeclarativeForm` and `<ResourceForm>`) on
937
- * purpose: two copies of the redaction branch is how a sensitive field ends up
938
- * redacted on one path and not the other.
939
- *
940
- * A field the caller did not declare is redacted rather than emitted — an
941
- * unknown field cannot be proven non-sensitive, and a create form can carry
942
- * values for fields seeded from outside the declaration. `fields` is caller
943
- * data too (its keys can be attacker-influenced the same way `before`/
944
- * `after` can), so the lookup uses `Object.hasOwn` rather than an
945
- * `=== undefined` check: a key like `toString` or `constructor` is NOT
946
- * "undefined" on a plain object — it resolves through the prototype chain to
947
- * a built-in function — and treating that as "known" would emit whatever
948
- * value rode in under that name raw.
949
- */
950
- declare function buildFormDiff(args: BuildFormDiffArgs): Record<string, AuditFieldDiff>;
951
- //#endregion
952
- //#region src/audit/use-form-submit-audit.d.ts
953
- interface FormSubmitAuditArgs {
954
- /** Values as the form opened — captured BEFORE the write, since a host may re-baseline after it. */
955
- readonly before: Readonly<Record<string, unknown>>;
956
- readonly after: Readonly<Record<string, unknown>>;
957
- readonly fields: Readonly<Record<string, unknown>>;
958
- readonly outcome: 'success' | 'error' | 'invalid';
959
- readonly resource?: string;
960
- readonly recordId?: string;
961
- readonly view?: 'create' | 'edit';
962
- /**
963
- * A backend-reported error code (e.g. `'backend-500'`, design doc §4.1) —
964
- * plumbing for a host to report WHY an `'error'` outcome happened, distinct
965
- * from `audit-diff-build-failed` below (which means the audit machinery
966
- * itself, not the submit, failed). Ignored on any other outcome.
967
- */
968
- readonly errorCode?: string;
969
- }
970
- interface FormSubmitAudit {
971
- /**
972
- * False when the app configured no sink. Hosts check it before copying their
973
- * values — that check is what makes an unaudited app pay nothing.
974
- */
975
- readonly enabled: boolean;
976
- readonly emit: (args: FormSubmitAuditArgs) => void;
977
- }
978
- /**
979
- * The single place a `form.submit` event is constructed.
980
1466
  *
981
- * Buro has two independent form hosts (`useDeclarativeForm` here and
982
- * `<ResourceForm>` in `@burojs/ui-shadcn`). They share this hook rather than a
983
- * copied block so the redaction branch cannot drift between them, and so a
984
- * third host does not become a third copy.
1467
+ * Only `app.*` events pass (REF-29): the framework's own events go through an internal emitter,
1468
+ * so an application can never forge a `form.submit` or a `flow.submit`. The `Audit` object
1469
+ * `createAudit` returns is unchanged for the application's own transport code.
985
1470
  */
986
- declare function useFormSubmitAudit(): FormSubmitAudit;
1471
+ declare function useAudit(): AppAudit | null;
987
1472
  //#endregion
988
1473
  //#region src/ui-settings/provider.d.ts
989
1474
  interface UiSettingsProviderProps {
@@ -1102,6 +1587,13 @@ declare function useLayerRuntime(): LayerRuntime;
1102
1587
  declare function useApplicationSnapshot(): ApplicationSnapshot;
1103
1588
  declare function useProjection(target: SnapshotEntityTargetInput): PortableProjection | undefined;
1104
1589
  declare function useLayerStatus(id: string): LayerStatus;
1590
+ /**
1591
+ * @internal The app's trusted block catalogue over the committed snapshot (P4-D): the built-in
1592
+ * blocks and every registered one. A flow's form and review tree compile against it, so
1593
+ * `l.place(appBlock)` resolves (spec 3.6). One lookup per snapshot, so a compile memoised by the
1594
+ * lookup hits across renders.
1595
+ */
1596
+ declare function useFlowFormCatalog(): BlockLookup;
1105
1597
  //#endregion
1106
1598
  //#region src/context.d.ts
1107
1599
  declare const I18nContext: import("react").Context<I18nFacade | null>;
@@ -1116,6 +1608,8 @@ interface ResourceContextValue {
1116
1608
  resource?: string;
1117
1609
  }
1118
1610
  declare const ResourceContext: import("react").Context<ResourceContextValue>;
1611
+ /** Where the chosen locale is persisted unless `<BuroProvider languageStorageKey>` says otherwise. */
1612
+ declare const DEFAULT_LANGUAGE_STORAGE_KEY = "buro:language";
1119
1613
  //#endregion
1120
1614
  //#region src/data/action-data-access.d.ts
1121
1615
  /** Runtime-owned action DI. Descriptor identity always comes from the current lease. */
@@ -1127,6 +1621,101 @@ declare function useApp(): AppRuntime;
1127
1621
  //#region src/hooks/use-resource.d.ts
1128
1622
  declare function useResource(ref: ResourceRefInput): RegistryResource | undefined;
1129
1623
  //#endregion
1624
+ //#region src/frames/context.d.ts
1625
+ /** The app's frame catalogue; `{}` outside a provider. @internal */
1626
+ declare function useFrameImplementations(): FrameImplementationCatalog;
1627
+ //#endregion
1628
+ //#region src/frames/check-context.d.ts
1629
+ /** The kit's frame pieces a frame view places. */
1630
+ type FramePiece = 'RecordTitle' | 'RecordActions' | 'CopyId' | 'FormActions' | 'ListControls' | 'ListFilters' | 'ListFlows' | 'ListLive' | 'ListSavedViews' | 'ListColumns' | 'ListExport' | 'ListCreate' | 'ListBody' | 'ListBulkBar';
1631
+ /**
1632
+ * What a frame's pieces report to (`@internal`): the record frame host counts their mounts to
1633
+ * check a custom frame's obligations at runtime (P6-15), and `checkFrame` renders a view
1634
+ * alone with `stub` set, so each piece draws a marker instead of reading the record chrome.
1635
+ */
1636
+ interface FrameCheckValue {
1637
+ /** Draw `<span data-check-piece>` instead of the piece (`checkFrame`). */
1638
+ readonly stub: boolean;
1639
+ /** Records a mount; the returned function records the unmount. */
1640
+ mount(piece: FramePiece): () => void;
1641
+ }
1642
+ /** @internal */
1643
+ declare const FrameCheckContext: import("react").Context<FrameCheckValue | null>;
1644
+ /** @internal Records the calling piece's mount while it is mounted; returns the context. */
1645
+ declare function useFramePiece(piece: FramePiece): FrameCheckValue | null;
1646
+ //#endregion
1647
+ //#region src/frames/use-frame-t.d.ts
1648
+ /** A frame view's `t`: reads `frames.<id>.<key>` (the block convention, foundation 12). @internal */
1649
+ declare function useFrameT(id: string): FrameTranslate;
1650
+ //#endregion
1651
+ //#region src/blocks/use-placement.d.ts
1652
+ /** A placement as its host draws it (spec 8.1, 8.2). */
1653
+ interface PlacementView {
1654
+ readonly state: 'absent' | 'pending' | 'ready' | 'failed';
1655
+ /**
1656
+ * Refetches what failed. Absent for a refused prop and for a block without a view: Retry
1657
+ * cannot help them.
1658
+ */
1659
+ readonly retry?: () => void;
1660
+ readonly error?: unknown;
1661
+ readonly block: EntityRef;
1662
+ /** The app's view of the block (`<BuroProvider blockImplementations>`), if it has one. */
1663
+ readonly appImplementation?: BlockImplementation<any>;
1664
+ /** What the view is rendered with, once the placement is ready. */
1665
+ readonly viewProps?: BlockViewProps<unknown>;
1666
+ }
1667
+ /**
1668
+ * A placement of the screen being rendered, by its key (a nested placement by its derived key,
1669
+ * `<host>.<prop>`): its state over the plan (access, `when`, its sources) and its own mount (props
1670
+ * validated, `data` entries), the app's view of its block and the props the view receives.
1671
+ * `controller` defaults to the record frame's, else to the custom page's body around it.
1672
+ */
1673
+ declare function usePlacement(key: string, controller?: Pick<FormController, 'plan' | 'placement' | 'retry'>): PlacementView;
1674
+ //#endregion
1675
+ //#region src/forms/controller/use-page-screen.d.ts
1676
+ interface PageScreen {
1677
+ /** The page's one plan; empty for a failure marker. */
1678
+ readonly plan: ScreenPlan;
1679
+ /** Refetches only the failed requests under these source names (`block:<key>` for a placement). */
1680
+ retry(sources: readonly string[]): void;
1681
+ /** A placement's runtime at mount, by key. */
1682
+ placement(key: string): PlacementRuntime | undefined;
1683
+ /** Provides the page to `usePlacement(key)` below it, as a record frame does on a record. */
1684
+ readonly PlacementScope: (props: {
1685
+ readonly children: ReactNode;
1686
+ }) => ReactElement;
1687
+ }
1688
+ /**
1689
+ * @internal The resolver and planner half of a custom page's body (P1b+P1c decision 52), for the
1690
+ * page host: the screen's sources resolved with no record and no form values, the access of the
1691
+ * signed-in user (no resource rules: a page has none), and the page's one `planScreen` call, with
1692
+ * what is open from the surrounding `ScreenStateProvider` and the placements' block data. As on a
1693
+ * record, the plan's `blockDemand` is fed back to the resolver, so a placement under a panel whose
1694
+ * access is pending or denied never has its `data` requested. A `null` screen (a failure marker)
1695
+ * resolves and plans nothing.
1696
+ */
1697
+ declare function usePageScreen(compiled: CompiledScreen | null): PageScreen;
1698
+ //#endregion
1699
+ //#region src/forms/controller/route-source.d.ts
1700
+ /**
1701
+ * The `route` source of the screen below (spec 4.5): `{ id }` from the record route on show and
1702
+ * edit, `{}` on its create, a page's declared params from the page host. Only those routes
1703
+ * provide it; any other mount — a `<RecordScreen>` outside the record route, a nested screen —
1704
+ * reads `{}`. Values are what the route parsed (a page param's schema may coerce); `''` reads
1705
+ * `null` (the resolver normalises it).
1706
+ *
1707
+ * @internal Provided by `@burojs/ui-shadcn`'s record route and page host.
1708
+ */
1709
+ declare const RouteSourceContext: import("react").Context<Readonly<Record<string, unknown>> | null>;
1710
+ //#endregion
1711
+ //#region src/blocks/use-route-list-implementation.d.ts
1712
+ /**
1713
+ * The app's view of the built-in `buro:related-list` block (`<BuroProvider blockImplementations>`),
1714
+ * or `undefined`: an app entry replaces the list view everywhere, the route list included, so
1715
+ * `ResourceListPage` renders it instead of the built-in view.
1716
+ */
1717
+ declare function useRouteListImplementation(): BlockImplementation<any> | undefined;
1718
+ //#endregion
1130
1719
  //#region src/fields/provider.d.ts
1131
1720
  interface FieldImplementationProviderProps {
1132
1721
  readonly fieldImplementations?: FieldImplementationCatalog;
@@ -1145,6 +1734,29 @@ declare function useFieldImplementationCatalog(): FieldImplementationCatalog;
1145
1734
  declare function resolveFieldImplementation(catalog: FieldImplementationCatalog, input: UseFieldImplementationArgs): ResolvedFieldImplementation;
1146
1735
  declare function useFieldImplementation(input: UseFieldImplementationArgs): ResolvedFieldImplementation;
1147
1736
  //#endregion
1737
+ //#region src/fields/field-implementation.d.ts
1738
+ type MetaOf<S extends StandardSchemaV1> = InferSchemaOutput<S> extends Readonly<Record<string, unknown>> ? InferSchemaOutput<S> : never;
1739
+ /**
1740
+ * A field implementation whose `meta` is typed by a schema (P7 3.3): the schema's output is the
1741
+ * type of `props.meta` in every surface, and the framework applies the schema to the resolved
1742
+ * `meta` at render — a refusal renders the field misconfigured, never the implementation.
1743
+ *
1744
+ * ```tsx
1745
+ * const hsCode = fieldImplementation({
1746
+ * meta: z.object({ origin: z.string().nullable() }),
1747
+ * Input: ({ meta: { origin } }) => <CodePicker origin={origin} />,
1748
+ * });
1749
+ * ```
1750
+ */
1751
+ declare function fieldImplementation<const S extends StandardSchemaV1, T = unknown>(implementation: {
1752
+ readonly meta: S;
1753
+ readonly Input?: ComponentType<ScalarInputProps<T, MetaOf<S>>>;
1754
+ readonly Display?: ComponentType<ScalarDisplayProps<T, MetaOf<S>>>;
1755
+ readonly CollectionInput?: ComponentType<CollectionInputProps<T, MetaOf<S>>>;
1756
+ readonly CollectionDisplay?: ComponentType<CollectionDisplayProps<T, MetaOf<S>>>;
1757
+ readonly layout?: FieldImplementationLayout;
1758
+ }): FieldImplementation<T, MetaOf<S>>;
1759
+ //#endregion
1148
1760
  //#region src/fields/define-field-with-implementation.d.ts
1149
1761
  type FieldDeclarationOverrides<TMeta extends Readonly<Record<string, unknown>>> = Omit<FieldDeclaration, 'type' | 'format' | 'meta'> & {
1150
1762
  readonly meta?: TMeta;
@@ -1163,6 +1775,69 @@ interface DefinedField<TMeta extends Readonly<Record<string, unknown>> = Readonl
1163
1775
  */
1164
1776
  declare function defineFieldWithImplementation<TMeta extends Readonly<Record<string, unknown>> = Readonly<Record<string, unknown>>>(declaration: FieldDeclaration, implementation: FieldImplementation<any, TMeta>): DefinedField<TMeta>;
1165
1777
  //#endregion
1778
+ //#region src/fields/use-field-meta.d.ts
1779
+ /**
1780
+ * What a field's input receives as `meta` (P7 3.2, 3.3): its refs read from the owner's sources,
1781
+ * then the implementation's schema over the result. `pending` while a source or an asynchronous
1782
+ * schema is; `failed` once a source failed or the schema refused it, with the reason a
1783
+ * development message names.
1784
+ */
1785
+ type FieldMetaState = {
1786
+ readonly status: 'ready';
1787
+ readonly meta: Readonly<Record<string, unknown>>;
1788
+ } | {
1789
+ readonly status: 'pending';
1790
+ } | {
1791
+ readonly status: 'failed';
1792
+ readonly reason: string;
1793
+ };
1794
+ /**
1795
+ * The sources a field's `meta` reads when its owner supplies none of its own: `record` (the
1796
+ * record the field renders from, `null` in create), `values` (the live form values), `scope` and
1797
+ * `user`. An owner's own states (`extra`: a form controller's projected `record` and `values`, a
1798
+ * flow's `with` and `args`) replace these by name, so a field decided pending by P5 stays pending.
1799
+ *
1800
+ * @internal
1801
+ */
1802
+ declare function useFieldMetaSources(input: {
1803
+ readonly record: Readonly<Record<string, unknown>> | undefined;
1804
+ readonly values: Readonly<Record<string, unknown>> | undefined;
1805
+ readonly extra?: ScreenSources | undefined;
1806
+ }): ScreenSources;
1807
+ /**
1808
+ * A field's `meta` as its implementation receives it (P7 3.2, 3.3). `implementation` is the
1809
+ * catalogue entry the field resolved to: its `meta` schema, when it declares one, is applied to
1810
+ * the resolved meta, and an asynchronous schema keeps the state `pending` until it settles.
1811
+ * The same state object is returned while the values the refs read are the same.
1812
+ *
1813
+ * @internal
1814
+ */
1815
+ declare function useFieldMeta(field: Pick<FieldDeclaration, 'meta'>, implementation: FieldImplementation<unknown> | undefined, sources: ScreenSources): FieldMetaState;
1816
+ //#endregion
1817
+ //#region src/fields/use-candidates-access.d.ts
1818
+ /**
1819
+ * Whether a relation's select may read its target under its candidates (P7 3.4): every field a
1820
+ * candidate names must be one its user may view in the target. A predicate on a hidden field is
1821
+ * never sent — it would filter on data the user cannot see, and the rows offered would tell.
1822
+ */
1823
+ type CandidatesAccess = {
1824
+ readonly status: 'pending';
1825
+ } | {
1826
+ readonly status: 'allowed';
1827
+ } | {
1828
+ readonly status: 'forbidden';
1829
+ readonly field: string;
1830
+ };
1831
+ /**
1832
+ * `pending` while the permission map loads and a field decision depends on it; `forbidden`, with
1833
+ * the first such field, when a candidate names a field the user may not view; else `allowed`.
1834
+ * No candidates is `allowed` whatever the map says. A target the application lacks is closed
1835
+ * (every field hidden), so a candidate on it is `forbidden`.
1836
+ *
1837
+ * @internal
1838
+ */
1839
+ declare function useCandidatesAccess(target: string, candidates: readonly RelationCandidate[] | undefined): CandidatesAccess;
1840
+ //#endregion
1166
1841
  //#region src/hooks/use-doc.d.ts
1167
1842
  /**
1168
1843
  * Contextual documentation for one address, resolved at render time.
@@ -1370,12 +2045,11 @@ declare function useVisibilityCtx(): AppContext;
1370
2045
  * standalone" precedent: a headless hook meant to be instantiable via a bare
1371
2046
  * `renderHook(() => useX(...))`, with no wrapper, needs graceful
1372
2047
  * degradation rather than `useCtx`'s hard `must be used within a
1373
- * BuroProvider` throw. `useDeclarativeForm` (`../forms/use-declarative-
1374
- * form.ts`, Task 4) is the first consumer: a form with no conditional
1375
- * (`visible`/predicate `required`/`disabled`) fields must not need a full
1376
- * app `BuroProvider` tree just to mount and be unit-tested — Task 3's
1377
- * `renderHook(() => useDeclarativeForm(...))` tests (no wrapper) must keep
1378
- * passing once the controller starts reading a visibility ctx.
2048
+ * BuroProvider` throw. The form controller (`useFormController`) is the
2049
+ * consumer: a form with no predicate `required`/`disabled` fields must not
2050
+ * need a full app `BuroProvider` tree just to mount and be unit-tested —
2051
+ * `renderHook(() => useFormController(...))` tests (no wrapper) keep passing
2052
+ * while the controller reads a visibility ctx.
1379
2053
  *
1380
2054
  * Inside a real `BuroProvider`, this reads the same live resolver snapshot
1381
2055
  * `useVisibilityCtx` does (including reactivity to slice updates), just
@@ -1386,29 +2060,6 @@ declare function useVisibilityCtx(): AppContext;
1386
2060
  */
1387
2061
  declare function useOptionalVisibilityCtx(): AppContext;
1388
2062
  //#endregion
1389
- //#region src/hooks/use-visible-templates.d.ts
1390
- /**
1391
- * The document types a user may be offered for `resource` right now.
1392
- *
1393
- * `templates.items` filtered by each entry's `visible` predicate against the
1394
- * live app context (`{ permissions, scope, user }`) — the seam that makes a
1395
- * type set follow scope. The hook re-runs when the context slices move, so a
1396
- * picker built on it narrows the moment the user switches market, tenant or
1397
- * role, with no code in the picker that knows about any of them.
1398
- *
1399
- * Returns `[]` for an unknown resource and for a resource that declares no
1400
- * `templates` — "no types on offer", which is the honest answer for a resource
1401
- * that has none, and lets a caller render its picker unconditionally.
1402
- *
1403
- * ⚠️ **This is the OFFER, not an authorization boundary.** A type hidden here
1404
- * is still renderable by a record that already carries it (a row that exists
1405
- * has to stay readable) and still reachable by a hand-typed create URL — the
1406
- * page-level guard remains the host's, exactly as `ResourceConfig.visible`
1407
- * gates navigation rather than routes. Use a capability rule where the
1408
- * boundary has to hold.
1409
- */
1410
- declare function useVisibleTemplates(resource: string): DocumentTemplate[];
1411
- //#endregion
1412
2063
  //#region src/hooks/use-capability.d.ts
1413
2064
  interface UseCapabilityArgs {
1414
2065
  /** Resource whose rules apply. */
@@ -1460,6 +2111,131 @@ declare function useCapabilityResolver(resource: string, record?: unknown, mode?
1460
2111
  */
1461
2112
  declare function useCapability(capability: Capability, args: UseCapabilityArgs): CapabilityDecision;
1462
2113
  //#endregion
2114
+ //#region src/hooks/url-state/codec.d.ts
2115
+ type ResourceFilter = FilterExpression<ErasedResourceHandle>;
2116
+ type ResourceSort = SortExpression<ErasedResourceHandle>;
2117
+ /**
2118
+ * Deliberate bounds for untrusted URL AST payloads. The values are large
2119
+ * enough for an interactive list, while preventing a pasted URL from making
2120
+ * server rendering or the first client render do unbounded recursive work.
2121
+ */
2122
+ declare const RESOURCE_URL_STATE_LIMITS: {
2123
+ readonly maxEncodedBytes: 16384;
2124
+ readonly maxFilters: 50;
2125
+ readonly maxFilterDepth: 16;
2126
+ readonly maxFilterNodes: 200;
2127
+ readonly maxSortEntries: 20;
2128
+ };
2129
+ /** Typed, router-independent state for an interactive resource list. */
2130
+ /**
2131
+ * Suffixes of a list namespace that are not list state (P3-26): `?<namespace>.open=<id>`, a
2132
+ * placement's deep link, is page state. A list write keeps them; the codec ignores them.
2133
+ */
2134
+ declare const RESERVED_LIST_SUFFIXES: readonly ["open"];
2135
+ interface ResourceUrlState {
2136
+ filters: readonly ResourceFilter[];
2137
+ /** The builder group. `filters` above is the PINNED slot — always ANDed. */
2138
+ filterGroup: FilterGroup;
2139
+ sort: ResourceSort;
2140
+ page: number;
2141
+ pageSize: number;
2142
+ search: string;
2143
+ visibleColumns: string[] | null;
2144
+ }
2145
+ /**
2146
+ * Kept temporarily as a source-level migration seam for the list controller.
2147
+ * URL AST values preserve their JSON primitive type and no longer use this
2148
+ * hint for serialization or decoding.
2149
+ */
2150
+ type FieldType = 'string' | 'number' | 'boolean' | 'enum' | 'relation' | 'date';
2151
+ interface CodecOptions {
2152
+ /** Resource key — used as the parameter namespace prefix. */
2153
+ resource: string;
2154
+ /** Values equal to these defaults are omitted from the URL. */
2155
+ defaults: ResourceUrlState;
2156
+ /** @deprecated AST envelopes preserve primitive value types directly. */
2157
+ fieldTypes?: Record<string, FieldType>;
2158
+ }
2159
+ /** Serialize one resource's URL slice. AST state is versioned JSON/base64url. */
2160
+ declare function serializeResourceState(state: ResourceUrlState, opts: CodecOptions): URLSearchParams;
2161
+ /** Decode one resource's URL slice; malformed AST envelopes reset atomically. */
2162
+ declare function deserializeResourceState(params: URLSearchParams, opts: CodecOptions): ResourceUrlState;
2163
+ //#endregion
2164
+ //#region src/hooks/url-state/hidden-predicates.d.ts
2165
+ type Expression = FilterExpression<ErasedResourceHandle>;
2166
+ /** Every field an expression names, in order, duplicates kept out. */
2167
+ declare function filterFields(expression: Expression): readonly string[];
2168
+ //#endregion
2169
+ //#region src/hooks/use-field-access.d.ts
2170
+ /**
2171
+ * Every declared field's view and edit decision for the signed-in user (P5 spec 3.4) — the one
2172
+ * `resolveFieldAccess`, never a second branch. `reach: 'record'` decides over `row` (absent: a
2173
+ * create); `'every'` decides for every row at once (the record probe), and ignores `row`.
2174
+ * Memoised by the rules, the user, the scope, the permission map and the row (Known finding 19).
2175
+ * A loading map makes every guarded field `pending`; a resource the registry lacks decides
2176
+ * nothing (`{}`).
2177
+ */
2178
+ declare function useFieldAccess(resource: string, reach: FieldReach, row?: unknown): FieldAccessMap;
2179
+ /**
2180
+ * The one list predicate of a resource (P5 spec 3.7, decision P5-18): field view in any-record
2181
+ * reach and `column` rules, both with the record probe. Every list surface — the controller's
2182
+ * columns and filter offer, the table, the export dialog, the picker and the palette — reads it.
2183
+ * `pending` while the map loads and a decision depends on it; a resource the registry lacks is
2184
+ * closed (no field, no column).
2185
+ */
2186
+ declare function useListAccess(resource: string): ListAccess;
2187
+ /**
2188
+ * A list row as view code may read it (P5 spec 3.6.6, plan ruling 9): every field its user may
2189
+ * not view for every row left out — the id kept. For `renderCell`, `rowHref`, cell renderers and
2190
+ * selection-aware views; rules, item-action gates and action handlers keep the row as read
2191
+ * (policy). Memoised per row object while the decisions hold (Known finding 21: a row is never
2192
+ * projected with decisions computed for another).
2193
+ */
2194
+ declare function useViewRow(resource: string): <T extends object>(row: T) => T;
2195
+ //#endregion
2196
+ //#region src/hooks/use-projected-record.d.ts
2197
+ /** A record as view code may read it: the fields its user may view, or nothing yet. */
2198
+ interface ProjectedRecord {
2199
+ /** `pending` while a decision a read depends on is undecided, or the row is placeholder data. */
2200
+ readonly status: 'pending' | 'ready';
2201
+ /** The row without every field its user may not view (the id kept); `null` for no row. */
2202
+ readonly value: Readonly<Record<string, unknown>> | null;
2203
+ }
2204
+ /**
2205
+ * One record of `resource`, projected by field view in record reach (P5 spec 3.6): a title, a
2206
+ * recent label, a breadcrumb or a peek reads this and never the row as read. `pending` while any
2207
+ * decision is undecided (an undecided field is never shown) and for placeholder data — rows of the
2208
+ * previous record or page never decide a title (Known finding 21). Decisions are keyed by the row
2209
+ * object they were computed for.
2210
+ */
2211
+ declare function useProjectedRecord(resource: string, row: unknown, opts?: {
2212
+ readonly placeholder?: boolean;
2213
+ }): ProjectedRecord;
2214
+ /**
2215
+ * A record's title as view code may show it (P5 spec 3.6, decision P5-27): `useRecordTitle` over
2216
+ * the projected row, so a display field or a `recordTitle` hook never reads a field its user may
2217
+ * not view. A hidden or undecided title falls back to the `#id` form.
2218
+ */
2219
+ declare function useProjectedTitle(resource: string | undefined, id: unknown, row: unknown, opts?: {
2220
+ readonly placeholder?: boolean;
2221
+ }): string;
2222
+ //#endregion
2223
+ //#region src/policy-side-channel.d.ts
2224
+ /**
2225
+ * The record as read, for policy: item-action gates and the flows a record page starts.
2226
+ * `fallback` (the controller's own projected `record`) when none was attached.
2227
+ *
2228
+ * @internal `@burojs/ui-shadcn` reads it; view code never does.
2229
+ */
2230
+ declare function policyRecordOf(controller: object, fallback?: Readonly<Record<string, unknown>>): Readonly<Record<string, unknown>> | undefined;
2231
+ /**
2232
+ * The rows as read, for policy: selection, bulk and item-action gates. `fallback` (the
2233
+ * controller's own `rows`) when none was attached, as for a hand-written controller.
2234
+ *
2235
+ * @internal `@burojs/ui-shadcn` reads it; view code never does.
2236
+ */
2237
+ declare function policyRowsOf<T extends Record<string, unknown>>(controller: object, fallback: T[]): T[];
2238
+ //#endregion
1463
2239
  //#region src/hooks/use-availability.d.ts
1464
2240
  /**
1465
2241
  * One resolver bound to a resource, for callers that ask about MANY operations
@@ -1496,18 +2272,23 @@ declare function useAvailability(resource: string, operation: string, record?: u
1496
2272
  //#region src/hooks/use-app-actions.d.ts
1497
2273
  interface AppActionEntry {
1498
2274
  readonly name: string;
1499
- readonly label: {
1500
- single: string;
1501
- plural?: string;
1502
- };
1503
- readonly kind: 'modal' | 'link';
1504
- readonly component?: string;
2275
+ /** The declared default; a renderer resolves `appActions.<name>` in the bundle over it. */
2276
+ readonly label: string;
2277
+ /** `'flow'`: an app flow (P4); run it with `useAction({ appAction: name })`. */
2278
+ readonly kind: 'modal' | 'link' | 'flow';
2279
+ /** `kind: 'modal'`: the block the action presents (P7 3.16), with its portable props. */
2280
+ readonly block?: EntityRef;
1505
2281
  readonly props?: Readonly<Record<string, unknown>>;
1506
- readonly surface?: 'dialog' | 'sheet';
2282
+ readonly host?: 'dialog' | 'sheet';
1507
2283
  readonly to?: string;
1508
2284
  readonly icon?: string;
1509
2285
  /** Present when a rule allows it but disables it; the rule's own reason. */
1510
2286
  readonly disabledReason?: string;
2287
+ /**
2288
+ * A flow hosted on a custom page (`host: { page }`, P7 3.15): the page's own path. Its control
2289
+ * is a guarded link, not a runner; the entry exists only for a user who may open the page.
2290
+ */
2291
+ readonly hostHref?: string;
1511
2292
  }
1512
2293
  /**
1513
2294
  * Every app-level action (`defineAppAction`, `registerAppAction`) the
@@ -1525,6 +2306,10 @@ interface AppActionEntry {
1525
2306
  * the resource argument) without inventing a second gating engine next to
1526
2307
  * `useAvailabilityGate`.
1527
2308
  *
2309
+ * An app flow (`kind: 'flow'`, P4) is gated by the flow's own gate (`resolveFlowGate`: its
2310
+ * `requires`, its mutation's `requires`, and the same `action/run` rules at the same address),
2311
+ * the one gate its button and `useAction` run; while permissions load it is left out.
2312
+ *
1528
2313
  * Rule-hidden (or absent) actions are dropped from the list entirely; a rule
1529
2314
  * that allows-but-disables an action keeps it in the list with its
1530
2315
  * `disabledReason` set so a caller can render it inert instead of missing.
@@ -1538,12 +2323,14 @@ declare function useSetSlice<TName extends string = string>(name: TName): (value
1538
2323
  declare function useT(): (key: string, options?: {
1539
2324
  resource?: string;
1540
2325
  params?: Record<string, unknown>;
2326
+ count?: number;
1541
2327
  }) => string;
1542
2328
  /**
1543
2329
  * Provider-OPTIONAL translator. Behaves like {@link useT} when mounted inside a
1544
2330
  * `BuroProvider`, but does NOT throw when there is no i18n context —
1545
- * instead it returns the supplied `defaultValue` (interpolated with `params`),
1546
- * falling back to the raw key.
2331
+ * instead it returns the supplied `defaultValue` (interpolated with `params`,
2332
+ * and with `count` as `{{count}}` when one is given), falling back to the raw
2333
+ * key.
1547
2334
  *
1548
2335
  * Use this for components that ship in `@burojs/ui-shadcn` yet may render
1549
2336
  * standalone (outside the provider) — e.g. `<LoginForm>` on a pre-auth screen,
@@ -1554,13 +2341,13 @@ declare function useOptionalT(): (key: string, options?: {
1554
2341
  resource?: string;
1555
2342
  params?: Record<string, unknown>;
1556
2343
  defaultValue?: string;
2344
+ count?: number;
1557
2345
  }) => string;
1558
2346
  /**
1559
2347
  * The active locale code (e.g. `'ru'` / `'en'`), or `'en'` when no i18n
1560
- * provider is mounted. Powers per-locale DATA labels — resolve a
1561
- * `LocalizedText` (enum option labels, etc.) with `resolveLocalizedText(text,
1562
- * useLocale())`. Provider-optional so ui-shadcn components can render
1563
- * standalone.
2348
+ * provider is mounted. Powers locale-dependent formatting and re-renders a
2349
+ * component on a language switch. Provider-optional so ui-shadcn components
2350
+ * can render standalone.
1564
2351
  */
1565
2352
  declare function useLocale(): string;
1566
2353
  /**
@@ -1568,8 +2355,8 @@ declare function useLocale(): string;
1568
2355
  *
1569
2356
  * The difference from {@link useLocale} is the whole point: `useLocale`
1570
2357
  * collapses "the app is running in English" and "there is no app" into the same
1571
- * `'en'`, which is fine for resolving a `LocalizedText` (English is a defensible
1572
- * last resort for a label) and wrong for anything that has its own fallback to
2358
+ * `'en'`, which is fine for picking a label language (English is a defensible
2359
+ * last resort) and wrong for anything that has its own fallback to
1573
2360
  * fall back TO. A caller that already knows what "nobody chose a locale" means
1574
2361
  * for it — `@burojs/ui-shadcn`'s `Intl` formatters resolve a stored preference
1575
2362
  * in that case — must be able to tell the two apart, and with `useLocale` it
@@ -1587,6 +2374,11 @@ declare function useOptionalLocale(): string | undefined;
1587
2374
  * so the choice survives a real reload. No-op without a provider.
1588
2375
  */
1589
2376
  declare function useChangeLocale(): (locale: string) => void;
2377
+ /**
2378
+ * The `localStorage` key a language control persists the chosen locale under:
2379
+ * `<BuroProvider languageStorageKey>`, or `'buro:language'` without one.
2380
+ */
2381
+ declare function useLanguageStorageKey(): string;
1590
2382
  /**
1591
2383
  * Render-time translator, resource-scoped and provider-optional. The canonical
1592
2384
  * name for the i18n content model (react-admin `useTranslate` / i18next `t`).
@@ -1596,6 +2388,7 @@ declare function useChangeLocale(): (locale: string) => void;
1596
2388
  declare function useTranslate(): (key: string, options?: {
1597
2389
  resource?: string;
1598
2390
  params?: Record<string, unknown>;
2391
+ count?: number;
1599
2392
  }) => string;
1600
2393
  /**
1601
2394
  * The `has` + `t` capabilities bound to the ambient resource, for the pure
@@ -1657,8 +2450,20 @@ interface TestIdBuilder {
1657
2450
  listToolbar: (resource: string) => string;
1658
2451
  listAction: (resource: string, actionName: string) => string;
1659
2452
  listSearch: (resource: string) => string;
2453
+ /** The filter bar's "Some saved filters were not applied" notice (P5 spec 3.7). */
2454
+ listPredicatesDropped: (resource: string) => string;
2455
+ /** A list frame's slot, around its planned tree (P7-34). */
2456
+ listSlot: (resource: string, slot: string) => string;
2457
+ /** A resource-level flow on the route list's toolbar (P7-31). */
2458
+ listFlow: (resource: string, flowName: string) => string;
2459
+ /** The toolbar's overflow menu of resource-level flows. */
2460
+ listFlowsMenu: (resource: string) => string;
1660
2461
  listFilter: (resource: string, fieldName: string) => string;
1661
2462
  listTable: (resource: string) => string;
2463
+ /** A `related-list` card (`view: 'cards'`): the placement key and the row id. */
2464
+ relatedCard: (key: string, recordId: string) => string;
2465
+ /** A `related-list` timeline entry (`view: 'timeline'`): the placement key and the row id. */
2466
+ timelineEntry: (key: string, recordId: string) => string;
1662
2467
  listRow: (resource: string, recordId: string) => string;
1663
2468
  listCell: (resource: string, recordId: string, fieldName: string) => string;
1664
2469
  listRowAction: (resource: string, recordId: string, actionName: string) => string;
@@ -1694,39 +2499,64 @@ interface TestIdBuilder {
1694
2499
  pageEdit: (resource: string, recordId: string) => string;
1695
2500
  pageCreate: (resource: string) => string;
1696
2501
  /**
1697
- * The panel a detail surface renders in place of a field set when
1698
- * `ResourceConfig.templates.fallback: 'unsupported'` refuses an unrecognised
1699
- * document type. Keyed by RESOURCE and not by the offending value: the value
2502
+ * The notice a record screen renders for a record whose discriminator value
2503
+ * no declared type holds (P1b+P1c decision 64). Keyed by RESOURCE and not by the offending value: the value
1700
2504
  * is by definition one the application does not know, so a testid built from
1701
2505
  * it could not be written down in advance by the test that looks for it.
1702
2506
  */
1703
2507
  unsupportedDocumentType: (resource: string) => string;
1704
- resourceTab: (resource: string, tabKey: string, recordId?: string) => string;
1705
- subresourceTabs: (parentResource: string) => string;
1706
- subresourceTab: (tabKey: string) => string;
1707
- subresourceBody: (childResource: string) => string;
1708
- form: (resource: string, view: string, recordId?: string) => string;
1709
2508
  /**
1710
- * One declared field GROUP (`views.fields` as sections) — the `<section>`
1711
- * (or, when `collapsed`, the `<details>`) `<ResourceForm>` wraps each group
1712
- * in.
1713
- *
1714
- * ⚠️ Renamed from `formSection`, which composed `form-section:…` and had
1715
- * **no producer** — the component has always emitted `form-group:…` (SP9
1716
- * task 5, the same sweep that found the whole `picker-*` family addressing
1717
- * nothing).
2509
+ * A quick overlay (sheet or dialog; P3-21) by its top entry: `quick-sheet:<resource>:<id>`, or
2510
+ * `quick-sheet:<resource>` for a create.
2511
+ */
2512
+ quickSheet: (resource: string, recordId?: string) => string;
2513
+ /** The quick sheet's Back (when its history is deeper than one entry). */
2514
+ quickBack: () => string;
2515
+ /** The quick overlay's close button. */
2516
+ quickClose: () => string;
2517
+ /** The quick overlay's "Open as page" link. */
2518
+ quickOpenPage: () => string;
2519
+ /** A peek (the hover preview of a relation display's record, P3-22): `peek:<resource>:<key>`. */
2520
+ peek: (resource: string, key: string) => string;
2521
+ /**
2522
+ * A flow's host (P4 spec 4.15) and its controls, by the flow's identity
2523
+ * (`'<ns>:<resource>/actions/<name>'`, `'app/<name>'`): `flow-host:<flowId>`, and
2524
+ * `flow-<control>:<flowId>` for each control.
1718
2525
  */
1719
- formGroup: (resource: string, view: string, groupKey: string) => string;
2526
+ flowHost: (flowId: string) => string;
2527
+ /** The page-hosted flow's route body (P6): `page-flow:<flowId>`. */
2528
+ pageFlow: (flowId: string) => string;
2529
+ /** An inline flow's placement (P6): `inline-flow:<flowId>:<placement key>`. */
2530
+ inlineFlow: (flowId: string, key: string) => string;
2531
+ flowSubmit: (flowId: string) => string;
2532
+ flowBack: (flowId: string) => string;
2533
+ /** The footer's Cancel: closes through the dismiss guard. */
2534
+ flowCancel: (flowId: string) => string;
2535
+ /** The header's ✕, and an outcome screen's Close. */
2536
+ flowClose: (flowId: string) => string;
2537
+ /** The outcome message (rejected, failed, unknown, busy). */
2538
+ flowOutcome: (flowId: string) => string;
2539
+ flowCheckAgain: (flowId: string) => string;
2540
+ flowSendAsNew: (flowId: string) => string;
2541
+ flowStartOver: (flowId: string) => string;
2542
+ flowResend: (flowId: string) => string;
2543
+ /** The draft-found banner of a flow's form, and its two buttons (`:restore`, `:discard`). */
2544
+ flowDraft: (flowId: string) => string;
2545
+ /** A bulk flow's per-outcome summary. */
2546
+ bulkSummary: (resource: string, actionName: string) => string;
2547
+ form: (resource: string, view: string, recordId?: string) => string;
1720
2548
  formField: (resource: string, view: string, fieldName: string) => string;
1721
2549
  formFieldInput: (resource: string, view: string, fieldName: string) => string;
1722
2550
  formFieldError: (resource: string, view: string, fieldName: string) => string;
1723
2551
  formFieldLocked: (resource: string, view: string, fieldName: string) => string;
2552
+ /** A field leaf its user may not read the value of: the label and the restriction, no value. */
2553
+ formFieldRestricted: (resource: string, view: string, fieldName: string) => string;
1724
2554
  /**
1725
2555
  * A field's help text — the hint under the input, resolved from the i18n
1726
- * `hint` slot or from `meta.description`.
2556
+ * `fields.<name>.description` slot or from the field's description.
1727
2557
  *
1728
2558
  * ⚠️ This one was the other way round: the id was spelled identically on
1729
- * both sides and simply never rendered. `<ResourceForm>` drew the hint
2559
+ * both sides and simply never rendered. the old record form drew the hint
1730
2560
  * paragraph with an `id` (for `aria-describedby`) and no `data-testid`, so
1731
2561
  * `form.field(x).help()` had matched nothing since it was written. The
1732
2562
  * producer was added at SP9 task 5, which is what lets a browser test assert
@@ -1754,6 +2584,8 @@ interface TestIdBuilder {
1754
2584
  * Auto-save / draft-found banner (B7.1). Rendered above the form's
1755
2585
  * field stack on mount when a non-stale localStorage draft exists.
1756
2586
  */
2587
+ /** One option of the built-in type offer of a typed create (P1b+P1c decision 24). */
2588
+ typeOffer: (resource: string, type: string) => string;
1757
2589
  formDraftBanner: (resource: string, view: string) => string;
1758
2590
  /** Restore button inside the draft banner (B7.1). */
1759
2591
  formDraftRestore: (resource: string, view: string) => string;
@@ -1788,13 +2620,28 @@ interface TestIdBuilder {
1788
2620
  * element inside it that does.
1789
2621
  *
1790
2622
  * The trigger takes the TARGET resource, not (resource, view, field): the
1791
- * component composes it from `to`. Inside a `<ResourceForm>` the trigger's
2623
+ * component composes it from `to`. Inside a record form the trigger's
1792
2624
  * id is overridden with `formFieldInput(resource, view, field)` — the form
1793
2625
  * passes its own `testid` prop so a field's input is addressable the same
1794
2626
  * way whatever its kind — while every child of the popover keeps the
1795
2627
  * per-target prefix below. Both facts are asserted in that same test.
1796
2628
  */
1797
2629
  pickerTrigger: (targetResource: string) => string;
2630
+ /**
2631
+ * The select's "view" button (P6-28): the target resource and the form field that holds the key,
2632
+ * `relation-view:<target>:<field>`.
2633
+ */
2634
+ relationView: (targetResource: string, field: string) => string;
2635
+ /** The select's "+ create" button (P6-29): `relation-create:<target>:<field>`. */
2636
+ relationCreate: (targetResource: string, field: string) => string;
2637
+ /**
2638
+ * What the select says in place of its options while it must not read (P7 3.4): which field to
2639
+ * choose first, or that its candidates name a field the user may not see.
2640
+ * `relation-candidates-hint:<resource>:<field>`.
2641
+ */
2642
+ relationCandidatesHint: (resource: string, field: string) => string;
2643
+ /** A chip of a to-many select (P7 3.5): `relation-many-chip:<resource>:<field>:<id>`. */
2644
+ relationManyChip: (resource: string, field: string, id: string) => string;
1798
2645
  /** The popover's search box. */
1799
2646
  pickerSearch: (targetResource: string) => string;
1800
2647
  /** The popover's result list (the popover content itself carries no id). */
@@ -1815,12 +2662,6 @@ interface TestIdBuilder {
1815
2662
  pickerDialogSearch: (targetResource: string) => string;
1816
2663
  /** The disabled trigger rendered when the target resource is unregistered. */
1817
2664
  pickerUnregistered: (targetResource: string) => string;
1818
- actionConfirmDialog: (resource: string, scope: string, actionName: string, recordId?: string) => string;
1819
- actionConfirmYes: (resource: string, scope: string, actionName: string) => string;
1820
- actionConfirmNo: (resource: string, scope: string, actionName: string) => string;
1821
- formModal: (resource: string, scope: string, actionName: string, recordId?: string) => string;
1822
- formModalSubmit: (resource: string, scope: string, actionName: string) => string;
1823
- formModalCancel: (resource: string, scope: string, actionName: string) => string;
1824
2665
  childrenTable: (resource: string, view: string, fieldName: string) => string;
1825
2666
  childrenRow: (resource: string, view: string, fieldName: string, index: number) => string;
1826
2667
  childrenAdd: (resource: string, view: string, fieldName: string) => string;
@@ -1867,20 +2708,6 @@ declare function useTestIds(): TestIdBuilder;
1867
2708
  */
1868
2709
  declare function useRecordTitle(resource: string | undefined, id: unknown, record?: unknown): string;
1869
2710
  //#endregion
1870
- //#region src/hooks/use-action.d.ts
1871
- interface UseActionResult {
1872
- run: () => Promise<void>;
1873
- isRunning: boolean;
1874
- error: Error | null;
1875
- }
1876
- interface UseActionArgs<TRecord, R extends ResourceHandle<any, any, any, any> = ResourceHandle<any, any, any, any>> {
1877
- resource: R;
1878
- record: TRecord;
1879
- values?: Partial<TRecord>;
1880
- dispatcher: Dispatcher;
1881
- }
1882
- declare function useAction<TRecord = unknown, R extends ResourceHandle<any, any, any, any> = ResourceHandle<any, any, any, any>>(action: Action<TRecord>, args: UseActionArgs<TRecord, R>): UseActionResult;
1883
- //#endregion
1884
2711
  //#region src/hooks/use-task-run.d.ts
1885
2712
  /**
1886
2713
  * DRIVES ONE PIECE OF WORK AND KEEPS A {@link TaskRunReport} A VIEW CAN RENDER.
@@ -1893,8 +2720,8 @@ declare function useAction<TRecord = unknown, R extends ResourceHandle<any, any,
1893
2720
  *
1894
2721
  * That is deliberate and it is what makes this not an operations affordance.
1895
2722
  * The two callers this framework already has for it are a polled server-side
1896
- * run and a client-side `for` loop over selected rows (`BulkActionHandler`
1897
- * hand-rolls its own summary today) — nothing about a poll or a loop appears
2723
+ * run and a client-side `for` loop over selected rows (a bulk flow has its own
2724
+ * per-outcome summary since P4) — nothing about a poll or a loop appears
1898
2725
  * anywhere below.
1899
2726
  */
1900
2727
  /** What a running task tells the report. Handed to the task; never held afterwards. */
@@ -1964,6 +2791,16 @@ interface UseChecksOptions {
1964
2791
  readonly debounceMs?: number;
1965
2792
  readonly enabled?: boolean;
1966
2793
  readonly actionName?: string;
2794
+ /**
2795
+ * The screen's `with` sources the checks read, by name (P2 4.10). Pass a stable object: a
2796
+ * new one re-runs the inline pass.
2797
+ */
2798
+ readonly sources?: Readonly<Record<string, SourceState>>;
2799
+ }
2800
+ /** The values one authoritative pass evaluates, when they are not the render's. */
2801
+ interface CheckPassValues {
2802
+ readonly values: Record<string, unknown>;
2803
+ readonly saved?: Record<string, unknown>;
1967
2804
  }
1968
2805
  interface UseChecksResult {
1969
2806
  readonly inlineIssues: ValidationIssues;
@@ -1977,89 +2814,28 @@ interface UseChecksResult {
1977
2814
  * empty because nothing was evaluated — a caller MUST NOT read that as
1978
2815
  * "nothing fired" and proceed. Check `outcome.aborted` before
1979
2816
  * `outcome.blockers`.
2817
+ *
2818
+ * `over` evaluates the given values (and saved record) instead of the ones
2819
+ * this render passed: a submit decides on exactly the values it submits.
1980
2820
  */
1981
- readonly runNow: () => Promise<CheckOutcome>;
2821
+ readonly runNow: (over?: CheckPassValues) => Promise<CheckOutcome>;
1982
2822
  }
1983
2823
  declare function useChecks(options: UseChecksOptions): UseChecksResult;
1984
2824
  //#endregion
1985
- //#region src/checks/use-check-gate.d.ts
1986
- interface CheckGateRequest {
1987
- readonly surface: CheckSurface;
1988
- readonly values?: Record<string, unknown>;
1989
- readonly saved?: Record<string, unknown>;
1990
- readonly record?: Record<string, unknown>;
1991
- readonly actionName?: string;
1992
- /** An existing `action.confirm` message; becomes the dialog header. */
1993
- readonly header?: string | null;
1994
- }
1995
- interface CheckGateState {
2825
+ //#region src/hooks/use-refresh.d.ts
2826
+ interface UseRefreshResult {
2827
+ /** Invalidates every native data query and resolves once the active ones have refetched. */
2828
+ readonly refresh: () => Promise<void>;
2829
+ readonly isRefreshing: boolean;
1996
2830
  /**
1997
- * The resource whose checks this gate evaluates. Carried in the state so the
1998
- * dialog rendering it can bind its translations to the same resource the
1999
- * rules came from: a check message's i18n key is `checks.<id>`, which only
2000
- * reaches the per-resource entry `resources.<name>.checks.<id>` when a
2001
- * resource is bound.
2002
- *
2003
- * It must travel WITH the state rather than being passed to the dialog
2004
- * separately — the ambient `ResourceContext` is not a substitute. Only
2005
- * `<ResourceShow>` and `<ResourceForm>` mount one, so an action gated from a
2006
- * LIST ROW has no ambient resource at all, and the same check would render
2007
- * translated on a show page and untranslated in the list. (Exactly the bug
2008
- * `useResolvedLabel` in `action-button.tsx` carries a comment about.)
2009
- *
2010
- * Optional because a resource-less host — `<DeclarativeForm>` builds this
2011
- * shape by hand — genuinely has no resource to name.
2831
+ * False without a native data runtime: there is nothing to refresh and `refresh` is a no-op.
2832
+ * A bare `QueryClientProvider` does not count — only the data runtime's own query client holds
2833
+ * the queries this hook invalidates.
2012
2834
  */
2013
- readonly resource?: string;
2014
- readonly open: boolean;
2015
- readonly busy: boolean;
2016
- readonly header: string | null;
2017
- readonly blockers: ValidationIssues;
2018
- readonly confirmable: ValidationIssues;
2019
- readonly proceed: () => void;
2020
- readonly cancel: () => void;
2021
- }
2022
- interface UseCheckGateResult {
2023
- readonly run: (request: CheckGateRequest) => Promise<'proceed' | 'cancel'>;
2024
- readonly state: CheckGateState;
2025
- }
2026
- /**
2027
- * Imperative pre-flight gate. `run()` evaluates the resource's checks for the
2028
- * surface, and resolves immediately with 'proceed' when there is nothing to say.
2029
- * Otherwise it opens (the host renders the dialog off `state`) and resolves once
2030
- * the user decides. One dialog: an existing `action.confirm` becomes its header
2031
- * rather than a second prompt.
2032
- */
2033
- declare function useCheckGate(resource: string): UseCheckGateResult;
2034
- //#endregion
2035
- //#region src/hooks/use-bulk-action.d.ts
2036
- interface UseBulkActionResult {
2037
- /** Resolves with the dispatched result, or `undefined` if the dispatch itself failed. */
2038
- run: () => Promise<ActionResult | undefined>;
2039
- isRunning: boolean;
2040
- error: Error | null;
2041
- }
2042
- interface UseBulkActionArgs<TRecord, R extends ResourceHandle<any, any, any, any> = ResourceHandle<any, any, any, any>> {
2043
- resource: R;
2044
- records: TRecord[];
2045
- /** Every selected id — see {@link BulkActionDI.ids}. */
2046
- ids: string[];
2047
- dispatcher: Dispatcher;
2835
+ readonly available: boolean;
2048
2836
  }
2049
- /**
2050
- * Bulk variant of {@link useAction}. Wraps a `BulkActionHandler` so its
2051
- * result flows through the host dispatcher's effects pipeline (notify,
2052
- * clearSelection, refetch, redirect). Mirrors `useAction` API surface
2053
- * (`{ run, isRunning, error }`) so call sites stay symmetric.
2054
- *
2055
- * The dispatcher's `Action` union doesn't currently include
2056
- * `BulkActionHandler` (single-record vs bulk DI shape), but at runtime
2057
- * both share `kind: 'handler'` and an identical handler-returning-result
2058
- * contract. We cast at the dispatcher boundary so the runtime `kind`
2059
- * guard still passes; the dispatcher itself doesn't read the DI shape
2060
- * — handlers do — so the cast is sound.
2061
- */
2062
- declare function useBulkAction<TRecord = unknown, R extends ResourceHandle<any, any, any, any> = ResourceHandle<any, any, any, any>>(action: BulkActionHandler<TRecord>, args: UseBulkActionArgs<TRecord, R>): UseBulkActionResult;
2837
+ /** Refetches all data the app is showing: a header "Refresh" control, or any custom trigger. */
2838
+ declare function useRefresh(): UseRefreshResult;
2063
2839
  //#endregion
2064
2840
  //#region src/hooks/use-local-storage.d.ts
2065
2841
  /**
@@ -2082,14 +2858,17 @@ declare function useLocalStorage<T>(key: string, initial?: T): [T | undefined, (
2082
2858
  //#endregion
2083
2859
  //#region src/hooks/router/router-adapter.d.ts
2084
2860
  /**
2085
- * The framework's whole dependency on a router.
2861
+ * The framework's whole dependency on a router: where the app is and how to
2862
+ * move. It is the router's only binding — route matching, the active nav item,
2863
+ * breadcrumbs, URL state and every framework navigation read or write through
2864
+ * it — and `LinkComponentProvider` only renders anchors. Mount it with
2865
+ * `RouterAdapterProvider`.
2086
2866
  *
2087
- * It exists because `@burojs/react` used to import `next/navigation`
2088
- * directly, which made the package unusable outside Next for the sake of three
2089
- * hooks. Links were already injected through `LinkComponentProvider`; this is
2090
- * the same idea for location and navigation.
2867
+ * Every member is app-space: `pathname` without the router's basename, and
2868
+ * `push` / `replace` take a path the router prefixes itself.
2091
2869
  */
2092
2870
  interface RouterAdapter {
2871
+ /** The current path, without the router's basename. */
2093
2872
  pathname: string;
2094
2873
  searchParams: URLSearchParams;
2095
2874
  /** Navigate without adding a history entry — used for URL-state sync. */
@@ -2100,8 +2879,77 @@ interface RouterAdapterProviderProps {
2100
2879
  adapter: RouterAdapter;
2101
2880
  children: React.ReactNode;
2102
2881
  }
2882
+ /**
2883
+ * Binds your router to the framework. Required: mount it **above**
2884
+ * `<BuroProvider>`, once for the whole app. Without it no route matches
2885
+ * (`<NavAreaPage>` renders not-found and says so in development), no nav item
2886
+ * is active, and every framework navigation falls back to a full page load.
2887
+ * The default `redirect` and the navigation guard's "Leave" read the adapter
2888
+ * from where `<BuroProvider>` sits, so one mounted inside it is invisible to
2889
+ * them. `LinkComponentProvider` (`@burojs/ui-shadcn`) only renders anchors;
2890
+ * it is not a router binding.
2891
+ *
2892
+ * react-router (v7; v6 exports the same hooks from `react-router-dom`):
2893
+ *
2894
+ * ```tsx
2895
+ * import { RouterAdapterProvider } from '@burojs/react';
2896
+ * import * as React from 'react';
2897
+ * import { useLocation, useNavigate, useSearchParams } from 'react-router';
2898
+ *
2899
+ * export function ReactRouterAdapter({ children }: { children: React.ReactNode }) {
2900
+ * const location = useLocation();
2901
+ * const navigate = useNavigate();
2902
+ * const [searchParams] = useSearchParams();
2903
+ * const adapter = React.useMemo(
2904
+ * () => ({
2905
+ * pathname: location.pathname,
2906
+ * searchParams,
2907
+ * replace: (url: string) => navigate(url, { replace: true }),
2908
+ * push: (url: string) => navigate(url),
2909
+ * }),
2910
+ * [location.pathname, searchParams, navigate],
2911
+ * );
2912
+ * return <RouterAdapterProvider adapter={adapter}>{children}</RouterAdapterProvider>;
2913
+ * }
2914
+ * ```
2915
+ *
2916
+ * Next App Router (a client component):
2917
+ *
2918
+ * ```tsx
2919
+ * 'use client';
2920
+ * import { RouterAdapterProvider } from '@burojs/react';
2921
+ * import { usePathname, useRouter, useSearchParams } from 'next/navigation';
2922
+ * import * as React from 'react';
2923
+ *
2924
+ * export function NextRouterAdapter({ children }: { children: React.ReactNode }) {
2925
+ * const router = useRouter();
2926
+ * const pathname = usePathname();
2927
+ * const search = useSearchParams();
2928
+ * const adapter = React.useMemo(
2929
+ * () => ({
2930
+ * pathname,
2931
+ * searchParams: new URLSearchParams(search.toString()),
2932
+ * replace: (url: string) => router.replace(url, { scroll: false }),
2933
+ * push: (url: string) => router.push(url),
2934
+ * }),
2935
+ * [pathname, search, router],
2936
+ * );
2937
+ * return <RouterAdapterProvider adapter={adapter}>{children}</RouterAdapterProvider>;
2938
+ * }
2939
+ * ```
2940
+ *
2941
+ * Then: `<ReactRouterAdapter><BuroProvider app={app}>…</BuroProvider></ReactRouterAdapter>`.
2942
+ */
2103
2943
  declare function RouterAdapterProvider({ adapter, children }: RouterAdapterProviderProps): React.ReactElement;
2104
2944
  declare function useRouterAdapter(): RouterAdapter;
2945
+ /**
2946
+ * The router adapter, or `null` when none is mounted.
2947
+ *
2948
+ * `useRouterAdapter` throws by design — URL state that silently never persists
2949
+ * is worse than a crash. Audit is the opposite case: an app with no router is
2950
+ * legitimate, and auditing must never be the thing that breaks it.
2951
+ */
2952
+ declare function useOptionalRouterAdapter(): RouterAdapter | null;
2105
2953
  //#endregion
2106
2954
  //#region src/hooks/use-debounced-value.d.ts
2107
2955
  /**
@@ -2308,6 +3156,11 @@ interface UseEnumValuesResult {
2308
3156
  declare function useEnumValues(enumName: string): UseEnumValuesResult;
2309
3157
  //#endregion
2310
3158
  //#region src/hooks/relation-aggregator.d.ts
3159
+ /**
3160
+ * The most keys of one `by` read when the application declares no provider maximum: a batch asks
3161
+ * for `2 × keys` rows, so it is read in chunks of half this many keys.
3162
+ */
3163
+ declare const RELATION_BATCH_MAX_PAGE = 100;
2311
3164
  /**
2312
3165
  * A resolved relation record. Mirrors the shape used by the legacy
2313
3166
  * per-cell `useRelatedRecord` (see the deleted
@@ -2326,22 +3179,25 @@ interface UseRelatedRecordResult {
2326
3179
  * surfaces a per-id error (a dangling FK degrades to `record: undefined`
2327
3180
  * instead, matching today's fail-soft display behaviour). */
2328
3181
  error: Error | null;
3182
+ /**
3183
+ * True when the key matched more than one row of the target (a `by` key the backend does not
3184
+ * keep unique): `record` stays undefined, the display renders the key muted, and one
3185
+ * development error names the target, the field and the key. Never picks either row.
3186
+ */
3187
+ ambiguous: boolean;
2329
3188
  }
2330
- interface RelationAggregatorProviderProps {
2331
- children?: ReactNode;
3189
+ /** Options of `useRelatedRecord`: the target field the key holds, and the display's resolve. */
3190
+ interface UseRelatedRecordOptions {
3191
+ /** The target field the key is matched on; the target's id field when absent. */
3192
+ readonly by?: string;
3193
+ /** `'lazy'` registers nothing and reads nothing (spec 4.3); `'eager'` is the default. */
3194
+ readonly resolve?: 'eager' | 'lazy';
2332
3195
  }
2333
3196
  /**
2334
- * Mount once inside the native data runtime around the app
2335
- * content. Every `useRelatedRecord` call underneath registers its id here
2336
- * instead of holding its own react-query observer; this component renders
2337
- * one `<ResourceBatch>` per resource that currently has un-resolved
2338
- * registered ids.
2339
- */
2340
- declare function RelationAggregatorProvider({ children }: RelationAggregatorProviderProps): ReactElement;
2341
- /**
2342
- * Resolve a single record by `(targetResource, id)` for relation display.
3197
+ * Resolve a single record by `(targetResource, key)` for relation display: by the target's id
3198
+ * field, or by `options.by` (one batched `where <by> in [...]` list read per pair and flush).
2343
3199
  *
2344
- * With a `<RelationAggregatorProvider>` mounted above: registers demand
3200
+ * Under a `<BuroProvider>` with a data runtime (which mounts the aggregator): registers demand
2345
3201
  * (refcounted) and reads from the shared external store via
2346
3202
  * `useSyncExternalStore` — NO react-query observer per call. All calls for
2347
3203
  * the same resource on screen share ONE batched `getList(id in [...])`.
@@ -2355,52 +3211,16 @@ declare function RelationAggregatorProvider({ children }: RelationAggregatorProv
2355
3211
  * render; behaviour is selected by branching on VALUES (`store`, `idStr`),
2356
3212
  * never by skipping a hook call.
2357
3213
  */
2358
- declare function useRelatedRecord(targetResource: string, id: string | number | null | undefined): UseRelatedRecordResult;
3214
+ declare function useRelatedRecord(targetResource: string, id: string | number | null | undefined, options?: UseRelatedRecordOptions): UseRelatedRecordResult;
2359
3215
  //#endregion
2360
- //#region src/hooks/url-state/codec.d.ts
2361
- type ResourceFilter = FilterExpression<ErasedResourceHandle>;
2362
- type ResourceSort = SortExpression<ErasedResourceHandle>;
3216
+ //#region src/hooks/url-state/list-state-store.d.ts
2363
3217
  /**
2364
- * Deliberate bounds for untrusted URL AST payloads. The values are large
2365
- * enough for an interactive list, while preventing a pasted URL from making
2366
- * server rendering or the first client render do unbounded recursive work.
3218
+ * Where a list keeps its state (P1b+P1c decision 58): `'memory'`, which never touches the URL, or
3219
+ * the URL under a namespace (`{ url: 'lines' }` writes `?lines.page=2`).
2367
3220
  */
2368
- declare const RESOURCE_URL_STATE_LIMITS: {
2369
- readonly maxEncodedBytes: 16384;
2370
- readonly maxFilters: 50;
2371
- readonly maxFilterDepth: 16;
2372
- readonly maxFilterNodes: 200;
2373
- readonly maxSortEntries: 20;
3221
+ type ListState = 'memory' | {
3222
+ readonly url: string;
2374
3223
  };
2375
- /** Typed, router-independent state for an interactive resource list. */
2376
- interface ResourceUrlState {
2377
- filters: readonly ResourceFilter[];
2378
- /** The builder group. `filters` above is the PINNED slot — always ANDed. */
2379
- filterGroup: FilterGroup;
2380
- sort: ResourceSort;
2381
- page: number;
2382
- pageSize: number;
2383
- search: string;
2384
- visibleColumns: string[] | null;
2385
- }
2386
- /**
2387
- * Kept temporarily as a source-level migration seam for the list controller.
2388
- * URL AST values preserve their JSON primitive type and no longer use this
2389
- * hint for serialization or decoding.
2390
- */
2391
- type FieldType = 'string' | 'number' | 'boolean' | 'enum' | 'relation' | 'date';
2392
- interface CodecOptions {
2393
- /** Resource key — used as the parameter namespace prefix. */
2394
- resource: string;
2395
- /** Values equal to these defaults are omitted from the URL. */
2396
- defaults: ResourceUrlState;
2397
- /** @deprecated AST envelopes preserve primitive value types directly. */
2398
- fieldTypes?: Record<string, FieldType>;
2399
- }
2400
- /** Serialize one resource's URL slice. AST state is versioned JSON/base64url. */
2401
- declare function serializeResourceState(state: ResourceUrlState, opts: CodecOptions): URLSearchParams;
2402
- /** Decode one resource's URL slice; malformed AST envelopes reset atomically. */
2403
- declare function deserializeResourceState(params: URLSearchParams, opts: CodecOptions): ResourceUrlState;
2404
3224
  //#endregion
2405
3225
  //#region src/hooks/url-state/use-resource-url-state.d.ts
2406
3226
  /**
@@ -2485,7 +3305,7 @@ interface SetterOptions {
2485
3305
  *
2486
3306
  * Cross-resource isolation: params NOT prefixed with `<resource>.` are
2487
3307
  * preserved verbatim on every update. Two list views on the same page
2488
- * (subresource tabs / embedded lists) can each call this hook for their
3308
+ * (`related-list` placements in URL state) can each call this hook for their
2489
3309
  * own resource without colliding.
2490
3310
  *
2491
3311
  * Cold deep links: the initial render reads the router adapter's
@@ -2515,7 +3335,24 @@ interface SetterOptions {
2515
3335
  declare function useResourceUrlState(resource: string, opts?: {
2516
3336
  defaults?: Partial<ResourceUrlState>;
2517
3337
  fieldTypes?: Record<string, FieldType>;
3338
+ /**
3339
+ * Where the state lives: the URL under `?<resource>.` by default, the URL under another
3340
+ * namespace (`{ url: 'lines' }`), or `'memory'`, which never writes the URL.
3341
+ */
3342
+ state?: ListState;
3343
+ /**
3344
+ * The list's field view (P5 spec 3.7): a filter, group leaf or sort entry naming a field for
3345
+ * which `visible` is false is dropped when the state is read — before any chip or request —
3346
+ * and from every write. Once `ready` (the list's decisions are settled) the address is
3347
+ * rewritten without it, as a `replace`, through the same writer. Absent: nothing is dropped.
3348
+ */
3349
+ predicates?: {
3350
+ readonly visible: (field: string) => boolean;
3351
+ readonly ready: boolean;
3352
+ };
2518
3353
  }): ResourceUrlState & {
3354
+ /** How many predicates on hidden fields the address has carried and lost so far. */
3355
+ droppedPredicates: number;
2519
3356
  setFilters: (filters: readonly FilterExpression<ErasedResourceHandle>[], options?: SetterOptions) => void;
2520
3357
  setFilterGroup: (group: FilterGroup, options?: SetterOptions) => void;
2521
3358
  setSort: (sort: ResourceUrlState['sort'], options?: SetterOptions) => void;
@@ -2747,6 +3584,13 @@ declare function useSavedViews(resource: string): UseSavedViewsResult;
2747
3584
  */
2748
3585
  interface ApplyViewToUrlStateOptions {
2749
3586
  defaults?: Omit<Partial<ResourceUrlState>, 'page'>;
3587
+ /**
3588
+ * The list's field view (P5 spec 3.7): a filter, group leaf or sort entry the view (or a
3589
+ * default) carries on a field for which `visible` is false is dropped BEFORE any setter is
3590
+ * called, so a view saved by a user who may see the field never writes its value into the
3591
+ * address bar, history or a chip. Absent: nothing is dropped.
3592
+ */
3593
+ visible?: (field: string) => boolean;
2750
3594
  }
2751
3595
  /**
2752
3596
  * Subset of the `useResourceUrlState` return value the helper needs.
@@ -2810,7 +3654,9 @@ interface ApplyViewUrlStateSetters {
2810
3654
  * pageSize, search, visibleColumns). That keeps it trivially unit-testable
2811
3655
  * with a mock urlState.
2812
3656
  */
2813
- declare function applyViewToUrlState(view: SavedViewDef, urlState: ApplyViewUrlStateSetters, options?: ApplyViewToUrlStateOptions): void;
3657
+ declare function applyViewToUrlState(view: SavedViewDef, urlState: ApplyViewUrlStateSetters, options?: ApplyViewToUrlStateOptions): {
3658
+ readonly dropped: number;
3659
+ };
2814
3660
  //#endregion
2815
3661
  //#region src/hooks/command-palette/use-command-palette.d.ts
2816
3662
  /**
@@ -2875,122 +3721,57 @@ interface UseCommandPaletteOptions {
2875
3721
  */
2876
3722
  declare function useCommandPalette(opts?: UseCommandPaletteOptions): UseCommandPaletteResult;
2877
3723
  //#endregion
2878
- //#region src/hooks/command-palette/use-track-recent-record.d.ts
2879
- /**
2880
- * Storage shape for one entry in the "recently visited" list.
2881
- *
2882
- * - `resource` — registry name of the resource (e.g. `document_document`).
2883
- * - `id` — record id, always coerced to string for stable storage shape
2884
- * even when adopters use numeric ids.
2885
- * - `label` — human-readable display label resolved at write time
2886
- * (alias → name → title → label → fallback to id). Captured at write
2887
- * so the Recent group survives even if the record is later deleted
2888
- * server-side; the navigation will 404 but the entry still renders
2889
- * and identifies which record it referenced.
2890
- * - `visitedAt` — ISO timestamp of the most-recent visit. Updated on
2891
- * each call to `useTrackRecentRecord`.
2892
- */
2893
- interface RecentRecord {
2894
- resource: string;
2895
- id: string;
2896
- label: string;
2897
- visitedAt: string;
2898
- }
3724
+ //#region src/flows/row-batch.d.ts
2899
3725
  /**
2900
- * Persisted envelope. Mirrors the saved-views envelope (A4 pattern) so
2901
- * future schema changes can bump `version` without crashing older tabs.
2902
- *
2903
- * Reads accept three shapes (see `parseRecentEnvelope`):
2904
- * 1. Current v1 envelope `{ version: 1, items: RecentRecord[] }`.
2905
- * 2. Legacy / defensive bare-array form (migrated on next write).
2906
- * 3. Anything else / parse failure → treated as empty list.
3726
+ * One row's record as the flows of a list page see it (plan ruling 15): `covered` — every ref the
3727
+ * row flows' `when` and pre-click checks read is a field of the list DTO, so the list row is
3728
+ * the record; `ready` — the page's one batched read returned this row (`row`); `pending` — that
3729
+ * read has not answered for this id (or the id is not in the page's batch, Known finding 21).
2907
3730
  */
2908
- interface RecentEnvelope {
2909
- version: 1;
2910
- items: RecentRecord[];
3731
+ interface FlowRowState {
3732
+ readonly status: 'covered' | 'pending' | 'ready';
3733
+ readonly row?: Row;
2911
3734
  }
2912
3735
  /**
2913
- * Options for `useTrackRecentRecord`.
2914
- */
2915
- interface UseTrackRecentRecordOptions {
2916
- /**
2917
- * Maximum entries to keep. Default 5 (matches v0.2 spec C3.3). Older
2918
- * entries fall off the tail when a new one is upserted.
2919
- */
2920
- limit?: number;
2921
- /**
2922
- * Caller-supplied user id used as the localStorage key suffix. Multi-
2923
- * user installs pass `auth.user?.id`; single-user / unauthenticated
2924
- * adopters omit and share a `'singleton'` bucket.
2925
- *
2926
- * The framework's core `App` does not yet expose a built-in user
2927
- * slot (per the `AppContext` open interface — adopters declare their
2928
- * own `user` slice), so this is plumbed as an explicit option rather
2929
- * than read from `useApp()`. See `useApp` / `AppContext` for the
2930
- * declaration-merging extension point.
2931
- */
2932
- userId?: string | null;
2933
- isCurrentData?: boolean;
2934
- }
2935
- /**
2936
- * Records a "recently visited" entry for the given `(resource, record)`
2937
- * pair into `localStorage`.
2938
- *
2939
- * Storage contract:
2940
- * `localStorage["buro:recent:<userId|singleton>"]` →
2941
- * `JSON.stringify({ version: 1, items: RecentRecord[] })`
2942
- *
2943
- * Behaviour:
2944
- *
2945
- * - Fires on mount and re-fires whenever `record.id` changes — a
2946
- * single component instance navigating between two records still
2947
- * produces two entries.
2948
- * - Dedupes by `(resource, id)`: revisiting the same record bumps its
2949
- * `visitedAt` and moves it to the front instead of creating a
2950
- * duplicate.
2951
- * - FIFO-evicts the oldest entry when the list exceeds `limit`
2952
- * (default 5).
2953
- * - Waits for a real record before writing — calling with
2954
- * `record === undefined | null` (typical loading state) is a no-op.
2955
- * - SSR-safe: a missing `window` short-circuits before any storage
2956
- * call, so the hook is safe to import from server components.
2957
- * - Storage write errors (quota / private mode) are swallowed silently;
2958
- * the palette continues to function with whatever the last successful
2959
- * write left behind.
2960
- *
2961
- * Companion reader: `getRecentRecords(userId)` is the synchronous
2962
- * equivalent for the palette's Recent group — the palette re-reads on
2963
- * every render (cheap; a localStorage read is sub-microsecond).
2964
- *
2965
- * Example:
2966
- * ```tsx
2967
- * function DocumentShowPage({ params }: { params: { id: string } }) {
2968
- * const { record } = useFetchRecord('document_document', params.id);
2969
- * useTrackRecentRecord('document_document', record);
2970
- * return <ResourceShow record={record} />;
2971
- * }
2972
- * ```
2973
- */
2974
- declare function useTrackRecentRecord(resource: string, record: {
2975
- id?: unknown;
2976
- [key: string]: unknown;
2977
- } | null | undefined, opts?: UseTrackRecentRecordOptions): void;
2978
- /**
2979
- * Synchronous read of the Recent list for the given user. Safe to call
2980
- * during render; SSR-safe (returns `[]` when `window` is undefined).
2981
- *
2982
- * Typical use: the palette's Recent group calls this on every render
2983
- * to populate its rows. localStorage reads are sub-microsecond so the
2984
- * cost is negligible; we don't subscribe to storage events.
2985
- */
2986
- declare function getRecentRecords(userId: string | null | undefined): RecentRecord[];
3736
+ * The per-page batched pre-click read of a list (P4 spec 4.17, I8; plan ruling 15): one per
3737
+ * list page, around its rows. When every ref the page's row flows read in `when` and their
3738
+ * pre-click checks is a field of the list DTO, nothing is read and every row is `covered`;
3739
+ * otherwise the page's ids are read in ONE `many` (the runtime's query key holds the session,
3740
+ * the scope, the resource and the ids) and a row is `pending` until the read made for THIS page's
3741
+ * ids answered for it (Known finding 21: another page's answer never decides a row). A `with`
3742
+ * source is never read here: it loads at the click, or when a row menu opens.
3743
+ */
3744
+ declare function useFlowRowBatch(resource: string, ids: readonly string[], flows: readonly CompiledFlow[]): {
3745
+ readonly state: (id: string) => FlowRowState;
3746
+ readonly reader: ReactElement | null;
3747
+ };
3748
+ /** @internal The list page's batch, for the row flows below it. */
3749
+ interface FlowRowBatchValue {
3750
+ readonly resource: string;
3751
+ readonly state: (id: string) => FlowRowState;
3752
+ }
3753
+ /** @internal Provided by the list page (`ResourceList`) around its rows. */
3754
+ declare const FlowRowBatchContext: import("react").Context<FlowRowBatchValue | null>;
3755
+ /** What a row (or a record surface without a run) shows for one flow, before any click. */
3756
+ interface FlowRowControl {
3757
+ readonly state: FlowControlState;
3758
+ readonly reason?: string;
3759
+ /** The record the flow runs on: the batch's row when it read one, else the given row. */
3760
+ readonly record?: Row;
3761
+ /**
3762
+ * Why an `absent` control is absent: the flow's gate (no oracle: indistinguishable from a flow
3763
+ * that does not exist) or its own `when` over this record. A page-hosted flow's route answers
3764
+ * the first with not-found and the second with "not available" (P6).
3765
+ */
3766
+ readonly cause?: 'gate' | 'when';
3767
+ }
2987
3768
  /**
2988
- * Clear the Recent list for the given user. Used by adopters that want
2989
- * to wire a "Clear recent" affordance (out of scope for v0.2 but cheap
2990
- * to expose). Returns `true` when the key was removed (or already
2991
- * absent), `false` when localStorage threw.
3769
+ * @internal What a flow's control shows on a row, decided without a run and without a request
3770
+ * (4.17, I8): the gate (undecided while permissions load), then the row's batch state (pending
3771
+ * until the page's one read answered), then `when` and the pre-click checks over the row. A
3772
+ * condition reading a `with` name is decided at click, when the run reads it.
2992
3773
  */
2993
- declare function clearRecentRecords(userId: string | null | undefined): boolean;
3774
+ declare function useFlowRowResolver(resource: string): (name: string, record: Row | undefined) => FlowRowControl;
2994
3775
  //#endregion
2995
3776
  //#region src/hooks/use-item-actions.d.ts
2996
3777
  /**
@@ -3007,12 +3788,16 @@ interface ResolvedItemAction extends ItemActionPlacement {
3007
3788
  variant: 'default' | 'destructive';
3008
3789
  disabled: boolean;
3009
3790
  disabledReason?: string;
3791
+ /**
3792
+ * A flow (P4-C): its control as the row decides it before any click — the flow's gate, the
3793
+ * page's batched read and its pre-click checks (`undecided` and `pending` render busy).
3794
+ */
3795
+ flow?: FlowRowControl;
3010
3796
  }
3011
3797
  declare const RENDER_TIME_DATA: ActionDataAccess;
3012
3798
  /**
3013
- * Build a minimal ActionDI for evaluating action callbacks (link.to, modal.props,
3014
- * confirm message, `visible`, etc.). Mirrors `useAction` but without dispatcher
3015
- * coupling — link/modal kinds never hit the dispatcher.
3799
+ * Build a minimal ActionDI for evaluating a link's `to` and a modal's `props` functions at
3800
+ * render time.
3016
3801
  *
3017
3802
  * MOVED here from `@burojs/ui-shadcn`'s `action-button.tsx` (Task 6), which
3018
3803
  * defined this identically as a package-private function. `useItemActions` and
@@ -3160,9 +3945,12 @@ type FilterKind = {
3160
3945
  {
3161
3946
  kind: 'enum-values';
3162
3947
  values: readonly string[];
3163
- } | {
3948
+ } |
3949
+ /** `by`: the target field the column holds (`meta.by`); options write its values. */
3950
+ {
3164
3951
  kind: 'relation';
3165
3952
  to: string;
3953
+ by?: string;
3166
3954
  };
3167
3955
  /**
3168
3956
  * One filterable field as surfaced by `filters.available` — the
@@ -3220,7 +4008,34 @@ interface ResourceListController {
3220
4008
  */
3221
4009
  def: ResourceDef | undefined;
3222
4010
  /**
3223
- * The rows on screen, each coerced to {@link RowRecord}. Empty array
4011
+ * The one list predicate (P5 spec 3.7): field view in any-record reach and `column` rules,
4012
+ * decided with the record probe. `pending` while the permission map loads and a decision
4013
+ * depends on it — the controller then offers no column, sends no read and reports loading.
4014
+ */
4015
+ access: ListAccess;
4016
+ /**
4017
+ * How many predicates on fields its user may not view the list dropped — from the address and
4018
+ * from applied saved views (P5 spec 3.7). A host shows one notice while it is above zero.
4019
+ */
4020
+ droppedPredicates: number;
4021
+ /**
4022
+ * The read is forbidden (P5 spec 3.7): an author predicate (`fixedFilters`) or a function
4023
+ * search names a field its user may not view. No request is sent; `rows` is empty and there is
4024
+ * no total.
4025
+ */
4026
+ isForbidden: boolean;
4027
+ /**
4028
+ * A row as view code may read it (P5 spec 3.6.6, plan ruling 9): every field its user may not
4029
+ * view for every row left out, the id kept; one object per row. {@link rows} are already
4030
+ * projected this way; `viewRow` projects a row that came from elsewhere (a selected record, a
4031
+ * row of a selection snapshot). Selection, bulk and item-action gates are policy and read the
4032
+ * rows as read, which this hook keeps off its public type.
4033
+ */
4034
+ viewRow: (row: RowRecord) => RowRecord;
4035
+ /**
4036
+ * The rows on screen, each coerced to {@link RowRecord} and projected by its user's field
4037
+ * access (P5 spec 3.6.6): a field the user may not view for every row is not on a row, so a
4038
+ * list slot, a card grid or a custom body reading `rows` cannot draw it. Empty array
3224
4039
  * (never `undefined`) while loading or on a resource with no rows.
3225
4040
  * Moved from the `rows` memo, `_list-body.tsx:1310-1316`.
3226
4041
  *
@@ -3442,6 +4257,17 @@ interface ResourceListController {
3442
4257
  /** Free-text search box value (`_list-body.tsx`'s `search`). */
3443
4258
  search: string;
3444
4259
  setSearch: (query: string) => void;
4260
+ /**
4261
+ * The search declaration the list composes `search` with: the controller's `search` option
4262
+ * (a placement's own search box), else the resource's `views.list.search`. An export and a
4263
+ * count compose with it, so they describe the rows the list shows.
4264
+ */
4265
+ searchDecl?: SearchDecl;
4266
+ /**
4267
+ * Whether the list has a search for this user: a declared search set with every field its
4268
+ * user may not view removed and none left is no search (P5 spec 3.7) — a host hides the box.
4269
+ */
4270
+ searchEnabled: boolean;
3445
4271
  /**
3446
4272
  * `true` when the search, any pinned filter, the builder group, or
3447
4273
  * {@link ResourceListController.providerQuery} is active. Moved from
@@ -3852,6 +4678,22 @@ interface UseResourceListOptions {
3852
4678
  * omit to use the declared value untouched.
3853
4679
  */
3854
4680
  refetchInterval?: number | false;
4681
+ /**
4682
+ * Where the list keeps its state (P1b+P1c decision 58): by default the URL under the
4683
+ * resource's registry key (`?<resource>.page=2`), so bookmarks keep working; `{ url: prefix }`
4684
+ * namespaces it under another prefix; `'memory'` never writes the URL (a list inside a custom
4685
+ * block view, which must not write the record page's query string).
4686
+ */
4687
+ state?: ListState;
4688
+ /**
4689
+ * The default columns, in place of the target's `views.list.columns` (a `related-list`
4690
+ * placement's `columns`). Column rules still apply.
4691
+ */
4692
+ columns?: readonly string[];
4693
+ /** The default page size, in place of the target's `views.list.pageSize`. */
4694
+ pageSize?: number;
4695
+ /** The search, in place of the target's `views.list.search` (a placement's own search box). */
4696
+ search?: SearchDecl;
3855
4697
  }
3856
4698
  /**
3857
4699
  * Data-connected list controller for `resource`.
@@ -3990,6 +4832,433 @@ declare function ResourceListProvider({ value, children }: ResourceListProviderP
3990
4832
  */
3991
4833
  declare function useResourceListContext(): ResourceListController;
3992
4834
  //#endregion
4835
+ //#region src/dispatch/dispatcher-context.d.ts
4836
+ interface DispatcherProviderProps {
4837
+ /** Merged per key over the enclosing dispatcher's effects (over the defaults at the root). */
4838
+ readonly effects?: Partial<DispatcherEffects>;
4839
+ readonly children: ReactNode;
4840
+ }
4841
+ /**
4842
+ * A nested dispatcher scope: `effects` merged per key over the enclosing
4843
+ * dispatcher's. For a subtree that needs a different effect, and for tests that
4844
+ * spy on effects. Must be rendered inside a `BuroProvider`.
4845
+ */
4846
+ declare function DispatcherProvider({ effects, children }: DispatcherProviderProps): ReactElement;
4847
+ /** The nearest dispatcher. `BuroProvider` always mounts one. */
4848
+ declare function useDispatcher(): Dispatcher;
4849
+ /**
4850
+ * @internal The effects the nearest dispatcher scope resolves to, read at call time: a nested
4851
+ * scope that replaces one effect (a quick entry's `redirect`) passes what it does not handle to
4852
+ * its parent's.
4853
+ */
4854
+ declare function useDispatcherEffects(): () => DispatcherEffects;
4855
+ //#endregion
4856
+ //#region src/ui/present-block.d.ts
4857
+ /** What a modal action opens (P7 3.16): the block screen's props, in a dialog or a sheet. */
4858
+ interface PresentBlockInput extends BlockScreenProps {
4859
+ readonly host: 'dialog' | 'sheet';
4860
+ /** The activation the block is opened under; the ambient one when omitted. */
4861
+ readonly activation?: Partial<ReturnType<typeof useCurrentUIActivation>>;
4862
+ }
4863
+ /**
4864
+ * @internal Opens a modal action's block in the UI kit's `screens.block` (P7 3.16): the framework's
4865
+ * own opener for the reserved `buro:block` key, which an app's `useUIPresent` refuses. The opener's
4866
+ * dismiss scope becomes the overlay's parent, as for every overlay (P3-30), so closing a quick
4867
+ * sheet asks about a dirty block above it. `{ ok: false }` when nothing could be presented.
4868
+ */
4869
+ declare function usePresentBlock(): (input: PresentBlockInput) => UIPresentResult;
4870
+ //#endregion
4871
+ //#region src/navigation/resolve-route.d.ts
4872
+ /**
4873
+ * Resolve a `RouteDescriptor` (string or object form) into an app-space URL.
4874
+ *
4875
+ * - string: returned as-is.
4876
+ * - object: the target's canonical href via `hrefFor` (nesting-aware: a
4877
+ * resource behind nav groups lives at its group-prefixed path), plus every
4878
+ * non-id `param` in the query and `filters` encoded in the target list's
4879
+ * own URL format. An unregistered target keeps the literal `/<resource>…`
4880
+ * form, so a descriptor pointing outside the registry still resolves.
4881
+ *
4882
+ * The one resolver every navigation goes through: link actions, the
4883
+ * dispatcher's default `redirect`, the record pages' navigation targets and
4884
+ * the navigation guard's "leave".
4885
+ */
4886
+ declare function resolveRoute(to: RouteDescriptor$1, registry?: AppRuntime['registry']): string;
4887
+ //#endregion
4888
+ //#region src/navigation/use-page-access.d.ts
4889
+ /**
4890
+ * The decision about a page's `requires` (P7 3.13): `allowed` — the user holds the pair, or the
4891
+ * page asks for none; `pending` — the permission map is absent or still loading, so nothing is
4892
+ * decided and nothing may be read; `denied` — the map is decided and the user lacks the pair
4893
+ * (a map that failed to load decides nothing in the user's favour either).
4894
+ */
4895
+ type PageAccess = 'allowed' | 'pending' | 'denied';
4896
+ /**
4897
+ * {@link usePageAccess} over a permission map. A page without `requires` is `allowed` whatever the
4898
+ * map says. Fail closed: no map, or a map still loading, is `pending`.
4899
+ */
4900
+ declare function pageAccessFor(page: {
4901
+ readonly requires?: PageRequires | undefined;
4902
+ }, permissions: PermissionMap | undefined): PageAccess;
4903
+ /**
4904
+ * Whether the current user may open `page` (P7 3.13). Every entry to a page (the sidebar, the
4905
+ * palette, the group index), the route itself and a flow hosted on the page ask this one question,
4906
+ * so a page the user cannot open has no entry and its route is the framework not-found.
4907
+ */
4908
+ declare function usePageAccess(page: {
4909
+ readonly requires?: PageRequires | undefined;
4910
+ }): PageAccess;
4911
+ //#endregion
4912
+ //#region src/flows/page-flow-gate.d.ts
4913
+ /**
4914
+ * A flow with no record (an app flow, or a resource-level flow) as a page asks about it (P7
4915
+ * 3.15): the flow compiled, and its gate (`resolveFlowGate`, P4 4.17) decided before anything is
4916
+ * read — `undecided` while the permission map loads, `absent` for a flow that is not offered.
4917
+ * `compiled` is `null` for a flow the application does not declare (fail closed: `absent`).
4918
+ */
4919
+ interface PageFlowGate {
4920
+ readonly answer: FlowGateAnswer;
4921
+ readonly compiled: CompiledFlow | null;
4922
+ }
4923
+ /**
4924
+ * The gate of a record-free flow. The same `resolveFlowGate` the app-action palette and the list
4925
+ * toolbar run, so a page offers a flow exactly where they do.
4926
+ */
4927
+ declare function usePageFlowGate(target: FlowTarget): PageFlowGate;
4928
+ //#endregion
4929
+ //#region src/flows/page-host.d.ts
4930
+ /** The page a flow's `host: { page }` names, when it names one (P7 3.15). */
4931
+ declare function hostPageRef(host: unknown): EntityRef | undefined;
4932
+ /** A flow's host page: where its buttons link, and whether the user may open it. */
4933
+ interface HostPage {
4934
+ /** The page's own path (the flow's buttons are guarded links to it). */
4935
+ readonly href: string;
4936
+ readonly access: PageAccess;
4937
+ }
4938
+ /**
4939
+ * The host page of a flow (P7 3.15), or `undefined` for a flow that is not hosted on one. A page
4940
+ * no layer declares is `denied` (fail closed): the flow's buttons are absent rather than links to
4941
+ * a not-found page. So is a page its own `visible`, or any group on its chain, hides: the route
4942
+ * renders not-found for it (`PageHost`), so the link would land there.
4943
+ */
4944
+ declare function hostPageFor(registry: RegistryReader, host: unknown, permissions: Parameters<typeof pageAccessFor>[1], ctx: AppContext): HostPage | undefined;
4945
+ /** {@link hostPageFor} for a component: the registry and the permission map from the context. */
4946
+ declare function useHostPage(host: unknown): HostPage | undefined;
4947
+ /**
4948
+ * A flow control's state conjoined with its host page's access (P7 3.15): a page the user cannot
4949
+ * open leaves no button (`absent`), an undecided one draws nothing yet (`undecided`).
4950
+ */
4951
+ declare function conjoinHostPage(state: FlowControlState, hostPage: HostPage | undefined): FlowControlState;
4952
+ //#endregion
4953
+ //#region src/navigation/use-route-navigate.d.ts
4954
+ interface RouteNavigateOptions {
4955
+ /** Navigate without adding a history entry. */
4956
+ readonly replace?: boolean;
4957
+ }
4958
+ type RouteNavigate = (to: RouteDescriptor$1, options?: RouteNavigateOptions) => void;
4959
+ /**
4960
+ * The framework's one navigation seam. Resolves a `RouteDescriptor` against the
4961
+ * app registry and asks the navigation guard's gate (P1b+P1c decision 30):
4962
+ * unblocked, or to the exact current URL, it navigates at once; with unsaved
4963
+ * changes it holds the navigation for the guard's prompt. Outside a
4964
+ * `NavigationGuardProvider` it navigates directly. Either way the navigation
4965
+ * is the mounted `RouterAdapter`'s `push` (`replace` when asked), or a document
4966
+ * navigation without one.
4967
+ */
4968
+ declare function useRouteNavigate(): RouteNavigate;
4969
+ //#endregion
4970
+ //#region src/navigation/navigation-guard.d.ts
4971
+ /**
4972
+ * Marks an anchor a quick opener owns (a relation display, a related-list row). The attribute
4973
+ * alone changes nothing: the guard leaves a click to its opener only when the opener has also
4974
+ * claimed the element (`quickAnchorRef`, internal), so an app's own `<a data-buro-quick>` is
4975
+ * guarded like any link. A claimed anchor whose click the opener does not take (no
4976
+ * `preventDefault`) is held by the guard after all, at the bubble phase.
4977
+ */
4978
+ declare const QUICK_ANCHOR_ATTRIBUTE = "data-buro-quick";
4979
+ /**
4980
+ * @internal The ref a framework quick opener puts on its anchor (with `QUICK_ANCHOR_ATTRIBUTE`):
4981
+ * its click goes to the opener, which opens an overlay or navigates through the guarded gate.
4982
+ */
4983
+ declare function quickAnchorRef(element: Element | null): void;
4984
+ interface NavigationGuardText {
4985
+ readonly title: string;
4986
+ readonly body: string;
4987
+ readonly stay: string;
4988
+ readonly leave: string;
4989
+ /** Leave's label when the prompt holds a dismissal (`reason: 'dismiss'`). */
4990
+ readonly discard: string;
4991
+ }
4992
+ /** The guard's strings: `navGuard.*` in the framework bundles, English literals without one. */
4993
+ declare function useNavigationGuardText(): NavigationGuardText;
4994
+ /**
4995
+ * What the guard is holding, for the registered dialog to answer: a navigation (`href` is
4996
+ * the app-space destination the user tried to reach) or the dismissal of an overlay holding
4997
+ * a dirty form (`href` is `null`). Narrow on `reason`.
4998
+ */
4999
+ type NavigationGuardPrompt = {
5000
+ /**
5001
+ * The labels of the active blockers that carry one (P6 3.12): an inline flow whose node is not
5002
+ * on screen but holds unsaved input. The dialog says so after its text.
5003
+ */
5004
+ readonly labels: readonly string[];
5005
+ } & ({
5006
+ readonly reason: 'navigation';
5007
+ /** App-space destination the user tried to reach. */
5008
+ readonly href: string;
5009
+ /** Leave without saving: performs the deferred navigation. */
5010
+ leave(): void;
5011
+ /** Stay: drops the pending navigation. */
5012
+ stay(): void;
5013
+ } | {
5014
+ readonly reason: 'dismiss';
5015
+ readonly href: null;
5016
+ /** Discard: releases the overlay's forms and closes it with every overlay above it. */
5017
+ leave(): void;
5018
+ /** Stay: keeps every overlay open and gives focus back. */
5019
+ stay(): void;
5020
+ });
5021
+ /**
5022
+ * @internal Holds every route change while `active` (P4 plan ruling 7): a flow's pending lock
5023
+ * spans the write, so a framework navigation or an in-app anchor click is dropped without a
5024
+ * prompt (a development `console.info` names the hold) and `beforeunload` is armed. Registered
5025
+ * in an effect, so a React StrictMode remount registers it again (Known finding 20).
5026
+ */
5027
+ declare function useNavigationHold(active: boolean): void;
5028
+ /** What `useNavigationBlocker` takes besides `active`. */
5029
+ interface NavigationBlockerOptions {
5030
+ /**
5031
+ * Names what holds the unsaved input when its node is not on screen: the guard's dialog adds
5032
+ * `navigation.unsavedAction` and the label (P6 3.12).
5033
+ */
5034
+ readonly label?: string;
5035
+ }
5036
+ /** What `useNavigationBlocker` returns. */
5037
+ interface NavigationBlockerHandle {
5038
+ /** Unregister now, synchronously; the blocker registers again the next time `active` turns true. */
5039
+ release(): void;
5040
+ }
5041
+ /**
5042
+ * Arm the guard while the calling component holds unsaved changes (a dirty
5043
+ * form). A no-op outside a `BuroProvider`. `release()` disarms it at once — a
5044
+ * successful submit calls it before the navigation that follows, which must
5045
+ * not wait for a render to unregister the blocker.
5046
+ */
5047
+ declare function useNavigationBlocker(active: boolean, opts?: NavigationBlockerOptions): NavigationBlockerHandle;
5048
+ /**
5049
+ * Register the calling component as the guard's leave dialog for as long as
5050
+ * it is mounted, and return the navigation being held — to the CURRENT dialog
5051
+ * only: the most recently registered one still mounted. Every other
5052
+ * registrant gets `null`, so two dialogs (`<AppLayout>`'s and an explicit
5053
+ * `<NavigationGuardDialog>`, say) never stack; unmounting the current one hands
5054
+ * the prompt back to the previous registrant. With no dialog registered the
5055
+ * guard asks through `window.confirm`.
5056
+ */
5057
+ declare function useRegisterNavigationGuardDialog(): NavigationGuardPrompt | null;
5058
+ //#endregion
5059
+ //#region src/navigation/dismiss-scope.d.ts
5060
+ /**
5061
+ * One overlay's dismiss scope: a presentation instance, a flow's host, the export
5062
+ * dialog, or an app dialog under `useDismissGuard`. Scopes form a tree (P3-30): a dialog
5063
+ * rendered inside an overlay is a child of the enclosing scope, and an overlay presented
5064
+ * from inside another is a child of the scope it was presented from. A scope is dirty while
5065
+ * any navigation blocker in it or in a descendant is active, and locked while any form in it
5066
+ * or in a descendant submits.
5067
+ */
5068
+ interface DismissScopeValue {
5069
+ readonly id: string;
5070
+ /**
5071
+ * Ask before leaving: a locked scope ignores the request, a clean one calls `onLeave` at
5072
+ * once, a dirty one holds it for the guard's prompt (Leave calls it, Stay drops it).
5073
+ */
5074
+ request(onLeave: () => void): void;
5075
+ /** True while a form in this scope or a descendant scope submits. */
5076
+ isLocked(): boolean;
5077
+ }
5078
+ /**
5079
+ * Provide `value` as the dismiss scope of everything below: the forms rendered inside register
5080
+ * their unsaved-changes blockers and their submitting state in it.
5081
+ */
5082
+ declare function DismissGuardScope({ value, children }: {
5083
+ readonly value: DismissScopeValue;
5084
+ readonly children: ReactNode;
5085
+ }): ReactElement;
5086
+ /** What `useDismissGuard` returns. */
5087
+ interface DismissGuard {
5088
+ /** Close through the guard: asks first when a form in the scope (or below it) is dirty. */
5089
+ dismiss(): void;
5090
+ /** Pass to `<DismissGuardScope value={…}>` around the dialog's content. */
5091
+ readonly scope: DismissScopeValue;
5092
+ }
5093
+ /**
5094
+ * Guard an app's own dialog against losing a dirty form (P3-33): wrap its content in
5095
+ * `<DismissGuardScope value={guard.scope}>` and route its close through `guard.dismiss()`.
5096
+ * Outside a `BuroProvider` `dismiss()` calls `onDismiss` at once.
5097
+ *
5098
+ * ```tsx
5099
+ * const guard = useDismissGuard(() => setOpen(false));
5100
+ * <Dialog open={open} onOpenChange={(next) => { if (!next) guard.dismiss(); }}>
5101
+ * <DismissGuardScope value={guard.scope}><MyForm /></DismissGuardScope>
5102
+ * </Dialog>
5103
+ * ```
5104
+ */
5105
+ declare function useDismissGuard(onDismiss: () => void): DismissGuard;
5106
+ /**
5107
+ * @internal Lock the enclosing dismiss scope while `pending` is true: a submitting form's
5108
+ * overlay ignores Escape, an outside click and ✕ (P3-34). A no-op outside a scope.
5109
+ */
5110
+ declare function useDismissPending(pending: boolean): void;
5111
+ //#endregion
5112
+ //#region src/flows/open-implementation.d.ts
5113
+ /**
5114
+ * @internal The app's view of the block a flow's `then.open` names (P4 spec 4.13), by its
5115
+ * `<namespace>:<name>` id, from the catalog mounted in `<BuroProvider blockImplementations>`;
5116
+ * `undefined` when the app implements none (the UI kit falls back to its own).
5117
+ */
5118
+ declare function useFlowOpenImplementation(id: string): BlockImplementation<unknown> | undefined;
5119
+ //#endregion
5120
+ //#region src/flows/resource-flows.d.ts
5121
+ /** The surfaces a resource-level flow can be drawn on, besides the record ones (P7-31). */
5122
+ type ResourceFlowSurface = 'list';
5123
+ /**
5124
+ * The resource-level flows (`actions.resource`) a resource draws on `surface`, in the order its
5125
+ * toolbar shows them: `order` first (ascending; a flow without one keeps its declaration
5126
+ * position among the unordered), then declaration order. A flow whose `surfaces` resolve to
5127
+ * `false` on `surface` is left out; the rest are `'menu'` or a prominent mode.
5128
+ */
5129
+ declare function resourceFlowsOn(def: Pick<ResourceDef, 'actions'>, surface: ResourceFlowSurface): readonly ActionFlow[];
5130
+ //#endregion
5131
+ //#region src/flows/present-flow.d.ts
5132
+ interface PresentFlowInput extends FlowStart {
5133
+ readonly host: 'dialog' | 'sheet';
5134
+ readonly label: string;
5135
+ readonly title?: string;
5136
+ }
5137
+ /**
5138
+ * @internal Opens a flow with a form or a review in the UI kit's host (P4 spec 4.15; P4-C review
5139
+ * I1): the host owns the run, so it outlives the control that opened it — a dirty form still
5140
+ * asks before it closes, and a pending write is never cut. The opener's dismiss scope is the
5141
+ * host's parent (closing a quick sheet asks about a dirty flow above it) and the opener's
5142
+ * dispatcher scope runs `then`. Resolves when the host closes, with the last outcome (`halted`
5143
+ * when there was none); `null` when nothing could be presented (one development error names a
5144
+ * missing host or screen).
5145
+ */
5146
+ declare function usePresentFlow(): (input: PresentFlowInput) => Promise<FlowResult> | null;
5147
+ //#endregion
5148
+ //#region src/quick/use-quick.d.ts
5149
+ /** What a quick open did: opened, opened nothing (with a development error), or waited for access. */
5150
+ type QuickOpen = 'opened' | 'absent' | 'undecided';
5151
+ /**
5152
+ * Opening a record in an overlay from code (spec 3.5). Every call runs the same entry gate as the
5153
+ * framework's openers; `'absent'` and `'undecided'` open nothing, and an undecided call is never
5154
+ * queued.
5155
+ */
5156
+ interface QuickApi {
5157
+ show(resource: ResourceRefInput, id: string | number): QuickOpen;
5158
+ edit(resource: ResourceRefInput, id: string | number): QuickOpen;
5159
+ /**
5160
+ * Opens `quick.create`; resolves with the created row, or `null` when the gate refused it or
5161
+ * the overlay closed without one. `defaults` are the caller's trusted tier; `input` is user
5162
+ * input (a search text the user typed): it fills the fields the create form holds active and
5163
+ * editable for this user, makes the form dirty, and is dropped for any other field.
5164
+ */
5165
+ create(resource: ResourceRefInput, opts?: {
5166
+ readonly defaults?: Readonly<Record<string, unknown>>;
5167
+ readonly input?: Readonly<Record<string, unknown>>;
5168
+ }): Promise<QuickSettledRow>;
5169
+ }
5170
+ /**
5171
+ * The one way to open a record in an overlay from code. `show` / `edit` with an id that is no
5172
+ * key (`null`, `''`, `NaN`, an object, a boolean) open nothing and log a development error, as
5173
+ * does a refused entry; `'undecided'` (the permission map is loading) opens nothing and is never
5174
+ * queued. A `show` of the record already on top of the topmost quick sheet opens nothing more.
5175
+ */
5176
+ declare function useQuick(): QuickApi;
5177
+ /**
5178
+ * @internal The framework openers' path to `quick.show` (a relation display, a related-list
5179
+ * row): the gate with the opener's row, then — inside a quick sheet, a push through the sheet's
5180
+ * scope (it asks when the sheet holds a dirty form); inside a quick dialog, the dialog closes
5181
+ * (through its scope) and the target opens as a new overlay beside it; elsewhere a new overlay
5182
+ * whose parent is the enclosing scope.
5183
+ */
5184
+ declare function useQuickOpener(): (target: string, id: string, opts?: {
5185
+ readonly row?: unknown;
5186
+ }) => QuickOpen;
5187
+ //#endregion
5188
+ //#region src/quick/quick-entry-context.d.ts
5189
+ /** What a component inside a quick overlay knows about it. */
5190
+ interface QuickEntryValue {
5191
+ readonly stack: QuickStack;
5192
+ readonly host: QuickHost;
5193
+ /** The overlay's presentation id in the runtime. */
5194
+ readonly presentationId: string;
5195
+ /** The overlay's dismiss scope: a push or a close inside it asks through it. */
5196
+ readonly scope: DismissScopeValue;
5197
+ }
5198
+ /** @internal Provided by the UI kit's quick screen around the entry it renders. */
5199
+ declare const QuickEntryContext: Context<QuickEntryValue | null>;
5200
+ /** The quick overlay the caller renders in, or `null` outside one. */
5201
+ declare function useQuickEntry(): QuickEntryValue | null;
5202
+ /**
5203
+ * @internal What the UI kit's quick screen provides as `QuickEntryContext`: its stack, the host it
5204
+ * was presented in (read off its descriptor), its presentation id and the presentation's dismiss
5205
+ * scope. Called by the quick screen, which renders inside its presentation instance.
5206
+ */
5207
+ declare function useQuickEntryValue(stack: QuickStack): QuickEntryValue;
5208
+ //#endregion
5209
+ //#region src/quick/entry-gate.d.ts
5210
+ /** Which entry an opener asks for: a quick entry's mode, or the peek. */
5211
+ type EntryKind = 'show' | 'edit' | 'create' | 'peek';
5212
+ /**
5213
+ * The entry gate's answer (spec 4.4). `open` names the host and the resolved target (a resource
5214
+ * delegating its show through `routesTo.show` opens the delegate's entry). `undecided` while the
5215
+ * user's permission map loads: the opener renders plain text and sends nothing. `absent`, `hidden`
5216
+ * and `disabled` open nothing, and say why.
5217
+ */
5218
+ type EntryAnswer = {
5219
+ readonly state: 'open';
5220
+ readonly host: UIPresentHost;
5221
+ readonly target: string;
5222
+ } | {
5223
+ readonly state: 'undecided';
5224
+ } | {
5225
+ readonly state: 'absent' | 'hidden' | 'disabled';
5226
+ readonly why: string;
5227
+ };
5228
+ /**
5229
+ * @internal The one entry gate every opener asks before any request (spec 4.4, 4.14):
5230
+ *
5231
+ * 1. static — the target resolves (following `routesTo.show` for `show` and `peek`),
5232
+ * `recordEntries` names the entry, and its host and screen are wired in
5233
+ * `presentationHosts`; otherwise `absent` (one development error per missing member);
5234
+ * 2. decided — `undecided` while the permission map has `status: 'loading'`. No map is
5235
+ * decided, and a map in `error` is decided by the rules (a `can()` against it fails closed);
5236
+ * 3. dynamic — `resolveAvailability` of the entry's operation (`show` for the peek) with the
5237
+ * opener's row when it holds one.
5238
+ */
5239
+ declare function useEntryGate(): (target: string, entry: EntryKind, row?: unknown) => EntryAnswer;
5240
+ //#endregion
5241
+ //#region src/notifications/notification-sink.d.ts
5242
+ interface NotificationSinkMessage {
5243
+ /** `info` is a plain notice that dismisses itself, never a progress indicator. */
5244
+ readonly type: 'success' | 'error' | 'info';
5245
+ readonly message: string;
5246
+ readonly description?: string;
5247
+ }
5248
+ /** Where the dispatcher's default `notify` delivers — a UI kit's toaster. */
5249
+ interface NotificationSink {
5250
+ open(notification: NotificationSinkMessage): void;
5251
+ }
5252
+ /**
5253
+ * Register `sink` for as long as the calling component is mounted. Outside a
5254
+ * `BuroProvider` it registers nothing and throws nothing, so a toaster mounted
5255
+ * at an app root keeps working standalone.
5256
+ *
5257
+ * `sink` must be stable (memoised, e.g. with `useMemo`): it is an effect
5258
+ * dependency, so an inline object re-registers on every render.
5259
+ */
5260
+ declare function useRegisterNotificationSink(sink: NotificationSink | null): void;
5261
+ //#endregion
3993
5262
  //#region src/merge-components.d.ts
3994
5263
  /**
3995
5264
  * Shallow merge of components objects. Overrides keys win; both inputs unchanged.
@@ -4000,7 +5269,7 @@ declare function mergeComponents<TBase extends Record<string, unknown>, TOver ex
4000
5269
  /** A resource's list/show/edit/create route. */
4001
5270
  interface ResourceRouteDescriptor {
4002
5271
  resource: string;
4003
- action: 'list' | 'show' | 'edit' | 'create';
5272
+ action: 'list' | 'show' | 'edit' | 'create' | 'flow';
4004
5273
  path: string;
4005
5274
  }
4006
5275
  /** A custom (non-resource) page's single route. */
@@ -4041,6 +5310,44 @@ declare const en: {
4041
5310
  readonly validation: {
4042
5311
  readonly requiredHint: "(required)";
4043
5312
  readonly required: "This field is required";
5313
+ readonly string: {
5314
+ readonly minLength: "Too short";
5315
+ readonly maxLength: "Too long";
5316
+ readonly pattern: "Invalid format";
5317
+ };
5318
+ readonly constraint: {
5319
+ readonly invalid: "Invalid value";
5320
+ };
5321
+ readonly number: {
5322
+ readonly minimum: "Value is too small";
5323
+ readonly maximum: "Value is too large";
5324
+ readonly exclusiveMinimum: "Value is too small";
5325
+ readonly exclusiveMaximum: "Value is too large";
5326
+ readonly multipleOf: "Value is not an allowed step";
5327
+ };
5328
+ readonly array: {
5329
+ readonly minItems: "Too few items";
5330
+ readonly maxItems: "Too many items";
5331
+ readonly uniqueItems: "Items must be unique";
5332
+ };
5333
+ readonly validator: {
5334
+ readonly invalid: "Invalid value";
5335
+ readonly exception: "Validation failed";
5336
+ };
5337
+ readonly schema: {
5338
+ readonly invalid: "Invalid value";
5339
+ readonly exception: "Validation failed";
5340
+ };
5341
+ readonly resource: {
5342
+ readonly invalid: "Invalid value";
5343
+ };
5344
+ readonly type: {
5345
+ readonly string: "Must be text";
5346
+ readonly number: "Must be a number";
5347
+ readonly boolean: "Must be yes or no";
5348
+ readonly array: "Must be a list";
5349
+ readonly object: "Must be an object";
5350
+ };
4044
5351
  };
4045
5352
  readonly actions: {
4046
5353
  readonly close: "Close";
@@ -4069,7 +5376,10 @@ declare const en: {
4069
5376
  };
4070
5377
  readonly confirm: {
4071
5378
  readonly delete: "Are you sure you want to delete?";
4072
- readonly deleteMany: "Are you sure you want to delete {{count}} records?";
5379
+ readonly deleteMany_one: "Are you sure you want to delete {{count}} record?";
5380
+ readonly deleteMany_few: "Are you sure you want to delete {{count}} records?";
5381
+ readonly deleteMany_many: "Are you sure you want to delete {{count}} records?";
5382
+ readonly deleteMany_other: "Are you sure you want to delete {{count}} records?";
4073
5383
  readonly discardChanges: "Discard unsaved changes?";
4074
5384
  readonly ok: "OK";
4075
5385
  readonly confirm: "Confirm";
@@ -4079,22 +5389,24 @@ declare const en: {
4079
5389
  readonly proceed: "Continue anyway";
4080
5390
  readonly back: "Go back";
4081
5391
  readonly blocked: "Cannot continue";
4082
- readonly exception: "A check failed to run";
5392
+ readonly couldNotVerify: "Could not verify \"{{checkId}}\"";
5393
+ readonly notVerified: "Check \"{{checkId}}\" failed to run";
4083
5394
  readonly parseError: "Check \"{{checkId}}\" has an invalid condition: {{detail}}";
4084
5395
  };
4085
5396
  readonly table: {
4086
- readonly selectedCount: "{{count}} selected";
5397
+ readonly selectedCount_one: "{{count}} selected";
5398
+ readonly selectedCount_few: "{{count}} selected";
5399
+ readonly selectedCount_many: "{{count}} selected";
5400
+ readonly selectedCount_other: "{{count}} selected";
4087
5401
  readonly empty: "No records";
4088
5402
  readonly loading: "Loading…";
4089
- readonly rowsSelected: "{{count}} selected";
5403
+ readonly rowsSelected_one: "{{count}} selected";
5404
+ readonly rowsSelected_few: "{{count}} selected";
5405
+ readonly rowsSelected_many: "{{count}} selected";
5406
+ readonly rowsSelected_other: "{{count}} selected";
4090
5407
  readonly selectAllRows: "Select all rows";
4091
5408
  readonly selectRow: "Select row";
4092
5409
  };
4093
- readonly show: {
4094
- readonly groups: {
4095
- readonly other: "Other";
4096
- };
4097
- };
4098
5410
  readonly boolean: {
4099
5411
  readonly yes: "Yes";
4100
5412
  readonly no: "No";
@@ -4149,7 +5461,10 @@ declare const en: {
4149
5461
  readonly clear: "Clear";
4150
5462
  readonly reset: "Reset";
4151
5463
  readonly title: "Filters";
4152
- readonly selectedCount: "{{count}} selected";
5464
+ readonly selectedCount_one: "{{count}} selected";
5465
+ readonly selectedCount_few: "{{count}} selected";
5466
+ readonly selectedCount_many: "{{count}} selected";
5467
+ readonly selectedCount_other: "{{count}} selected";
4153
5468
  readonly is: "is";
4154
5469
  readonly isNot: "is not";
4155
5470
  readonly contains: "contains";
@@ -4169,11 +5484,12 @@ declare const en: {
4169
5484
  readonly errors: {
4170
5485
  readonly required: "Required";
4171
5486
  readonly invalid: "Invalid value";
4172
- readonly tooLong: "No more than {{max}} characters";
4173
- readonly tooShort: "At least {{min}} characters";
4174
5487
  readonly failed: "Validation failed";
4175
5488
  readonly validatorFailed: "Could not validate the form. Please try again.";
4176
5489
  };
5490
+ readonly access: {
5491
+ readonly restricted: "No access";
5492
+ };
4177
5493
  readonly list: {
4178
5494
  readonly loadingShort: "Loading…";
4179
5495
  readonly notRegistered: "Resource not registered";
@@ -4183,19 +5499,19 @@ declare const en: {
4183
5499
  readonly errorDetails: "Details";
4184
5500
  readonly emptyTitle: "No {{items}} yet";
4185
5501
  readonly emptyBody: "Get started by creating your first {{item}}.";
5502
+ readonly emptyBodyReadOnly: "They will appear here once they are added.";
4186
5503
  readonly emptyFilteredTitle: "No matching {{items}}";
4187
5504
  readonly emptyFilteredBody: "No {{items}} match the current filter. Try clearing the filter to see everything.";
4188
5505
  readonly clearFilter: "Clear filter";
4189
5506
  readonly create: "+ Create {{item}}";
4190
5507
  readonly templateUnknown: "Unknown";
4191
5508
  readonly search: "Search";
5509
+ readonly predicatesDropped: "Some saved filters were not applied";
5510
+ readonly flowsMenu: "More actions";
4192
5511
  };
4193
- readonly tab: {
4194
- readonly details: "Details";
4195
- };
4196
- readonly tabBody: {
4197
- readonly empty: "Nothing to show here yet.";
4198
- readonly failed: "This tab failed to load.";
5512
+ readonly timeline: {
5513
+ readonly empty: "No entries";
5514
+ readonly undated: "Undated";
4199
5515
  };
4200
5516
  readonly breadcrumbs: {
4201
5517
  readonly create: "Create";
@@ -4206,10 +5522,25 @@ declare const en: {
4206
5522
  readonly resources: "Resources";
4207
5523
  readonly main: "Main navigation";
4208
5524
  readonly breadcrumb: "Breadcrumb";
4209
- readonly language: "Language";
4210
5525
  readonly toggleSidebar: "Toggle sidebar";
4211
5526
  readonly label: "Navigation";
4212
5527
  };
5528
+ readonly shell: {
5529
+ readonly refresh: "Refresh";
5530
+ readonly help: "Help";
5531
+ readonly scopeSwitcher: "Switch";
5532
+ readonly scopePlaceholder: "Select…";
5533
+ readonly account: "Account";
5534
+ };
5535
+ readonly theme: {
5536
+ readonly label: "Theme";
5537
+ readonly light: "Light";
5538
+ readonly dark: "Dark";
5539
+ readonly system: "System";
5540
+ };
5541
+ readonly language: {
5542
+ readonly label: "Language";
5543
+ };
4213
5544
  readonly pageHeader: {
4214
5545
  readonly backTo: "Back to {{label}}";
4215
5546
  readonly editTitle: "Edit {{label}}";
@@ -4221,6 +5552,10 @@ declare const en: {
4221
5552
  readonly emptyBody: "This page has no registered content.";
4222
5553
  readonly notFoundTitle: "Page not found";
4223
5554
  readonly notFoundBody: "This route doesn't match any registered page.";
5555
+ readonly recordLoading: "Loading…";
5556
+ readonly recordNotFound: "This record does not exist, or you do not have access to it.";
5557
+ readonly recordSessionExpired: "Your session has expired. Sign in again to continue.";
5558
+ readonly recordError: "The record could not be loaded.";
4224
5559
  };
4225
5560
  readonly notifications: {
4226
5561
  readonly region: "Notifications";
@@ -4230,6 +5565,13 @@ declare const en: {
4230
5565
  readonly unsupportedType: "Unsupported type";
4231
5566
  readonly unsupportedTypeHint: "The type “{{type}}” is not registered for this resource.";
4232
5567
  readonly backToPicker: "Back to picker";
5568
+ /** The built-in type offer of a typed create (P1b+P1c decision 24). */
5569
+ readonly typeOffer: {
5570
+ readonly title: "Choose a type";
5571
+ readonly hint: "The type decides which fields the record has.";
5572
+ readonly empty: "No type can be created here.";
5573
+ readonly change: "Change type";
5574
+ };
4233
5575
  };
4234
5576
  readonly taskRun: {
4235
5577
  readonly idle: "Not started";
@@ -4253,7 +5595,10 @@ declare const en: {
4253
5595
  };
4254
5596
  readonly toolbar: {
4255
5597
  readonly clearAll: "Clear all";
4256
- readonly clearAllAria: "Clear {{count}} active filters";
5598
+ readonly clearAllAria_one: "Clear {{count}} active filter";
5599
+ readonly clearAllAria_few: "Clear {{count}} active filters";
5600
+ readonly clearAllAria_many: "Clear {{count}} active filters";
5601
+ readonly clearAllAria_other: "Clear {{count}} active filters";
4257
5602
  readonly addFilter: "Add filter";
4258
5603
  readonly filterBy: "Filter by";
4259
5604
  readonly columns: "Columns";
@@ -4290,6 +5635,7 @@ declare const en: {
4290
5635
  readonly liveOff: "Auto-refresh: off";
4291
5636
  };
4292
5637
  readonly field: {
5638
+ readonly misconfigured: "This field is misconfigured";
4293
5639
  readonly removeFile: "Remove file";
4294
5640
  readonly pickFile: "Pick or drop a file";
4295
5641
  readonly dropHint: "Click to pick a file or drag-and-drop";
@@ -4301,12 +5647,13 @@ declare const en: {
4301
5647
  readonly form: {
4302
5648
  readonly systemFields: "System fields (read-only)";
4303
5649
  readonly validating: "Validating";
4304
- readonly wizardProgress: "Wizard progress";
4305
5650
  };
4306
5651
  readonly relation: {
4307
5652
  readonly select: "Select {{name}}…";
4308
5653
  readonly failed: "Failed to load options.";
4309
5654
  readonly noResults: "No results.";
5655
+ /** A lazy `by` relation whose key names no row, found when it is clicked (P3). */
5656
+ readonly unavailable: "Not available";
4310
5657
  };
4311
5658
  readonly columnMenu: {
4312
5659
  readonly sortSection: "Sort";
@@ -4327,13 +5674,23 @@ declare const en: {
4327
5674
  readonly body: "You have unsaved changes on this page. If you leave now, they will be lost.";
4328
5675
  readonly stay: "Stay";
4329
5676
  readonly leave: "Leave without saving";
5677
+ readonly discard: "Discard changes";
5678
+ };
5679
+ readonly navigation: {
5680
+ readonly unsavedAction: "An action on this screen has unsaved input";
4330
5681
  };
4331
5682
  readonly relationPicker: {
4332
5683
  readonly viewAll: "Open list";
4333
5684
  readonly clear: "Clear";
4334
5685
  readonly advancedSearch: "Advanced search";
4335
5686
  readonly title: "Select: {{name}}";
4336
- readonly create: "Create";
5687
+ readonly view: "View";
5688
+ readonly create: "+ Create";
5689
+ readonly createdNotFitting: "The record was created but does not fit this field";
5690
+ readonly createdNotSelected: "The record was created but could not be selected";
5691
+ readonly chooseFirst: "Choose {{label}} first";
5692
+ readonly cannotSearchBy: "You cannot search by {{label}}";
5693
+ readonly notMatching: "Does not match the current filter";
4337
5694
  readonly select: "Select";
4338
5695
  readonly prev: "Prev";
4339
5696
  readonly next: "Next";
@@ -4344,10 +5701,20 @@ declare const en: {
4344
5701
  readonly restorePrompt: "Restore?";
4345
5702
  readonly restore: "Restore";
4346
5703
  readonly discard: "Discard";
5704
+ readonly recordChanged: "Record changed since your draft";
4347
5705
  readonly timeAgoMoment: "a moment ago";
4348
- readonly timeAgoMinutes: "{{n}} minutes ago";
4349
- readonly timeAgoHours: "{{n}} hours ago";
4350
- readonly timeAgoDays: "{{n}} days ago";
5706
+ readonly timeAgoMinutes_one: "{{count}} minute ago";
5707
+ readonly timeAgoMinutes_few: "{{count}} minutes ago";
5708
+ readonly timeAgoMinutes_many: "{{count}} minutes ago";
5709
+ readonly timeAgoMinutes_other: "{{count}} minutes ago";
5710
+ readonly timeAgoHours_one: "{{count}} hour ago";
5711
+ readonly timeAgoHours_few: "{{count}} hours ago";
5712
+ readonly timeAgoHours_many: "{{count}} hours ago";
5713
+ readonly timeAgoHours_other: "{{count}} hours ago";
5714
+ readonly timeAgoDays_one: "{{count}} day ago";
5715
+ readonly timeAgoDays_few: "{{count}} days ago";
5716
+ readonly timeAgoDays_many: "{{count}} days ago";
5717
+ readonly timeAgoDays_other: "{{count}} days ago";
4351
5718
  readonly timeAgoRecently: "recently";
4352
5719
  };
4353
5720
  readonly commandPalette: {
@@ -4431,10 +5798,16 @@ declare const en: {
4431
5798
  };
4432
5799
  };
4433
5800
  readonly dialog: {
4434
- readonly titleFilter: "Export {{count}} rows by current filter";
5801
+ readonly titleFilter_one: "Export {{count}} row by current filter";
5802
+ readonly titleFilter_few: "Export {{count}} rows by current filter";
5803
+ readonly titleFilter_many: "Export {{count}} rows by current filter";
5804
+ readonly titleFilter_other: "Export {{count}} rows by current filter";
4435
5805
  /** `estimatedCount` is optional on `ExportDialogProps` — shown when a filter-scope export has no known row estimate. */
4436
5806
  readonly titleFilterUnknownCount: "Export rows by current filter";
4437
- readonly titleSelected: "Export {{count}} selected rows";
5807
+ readonly titleSelected_one: "Export {{count}} selected row";
5808
+ readonly titleSelected_few: "Export {{count}} selected rows";
5809
+ readonly titleSelected_many: "Export {{count}} selected rows";
5810
+ readonly titleSelected_other: "Export {{count}} selected rows";
4438
5811
  /** Defensive fallback: Task 8 gates the trigger on `available` before opening, so this is only reachable from a direct/programmatic mount. */
4439
5812
  readonly unavailable: "Export is not available for this resource.";
4440
5813
  /** Shown in place of the format selector while a deferred exporter registration (`ExporterLoader`) is still loading. */
@@ -4470,6 +5843,94 @@ declare const en: {
4470
5843
  readonly notify: {
4471
5844
  readonly deleted: "Record deleted";
4472
5845
  };
5846
+ readonly record: {
5847
+ readonly copyId: "Copy ID";
5848
+ };
5849
+ /** A quick overlay's header (P3): Back to the previous entry, close, open the record's page. */
5850
+ readonly quick: {
5851
+ readonly back: "Back to {{title}}";
5852
+ readonly close: "Close";
5853
+ readonly openAsPage: "Open as page";
5854
+ };
5855
+ /** A peek: the hover preview of a related record (P3). */
5856
+ readonly peek: {
5857
+ readonly failed: "Couldn't load";
5858
+ readonly unavailable: "Not available";
5859
+ };
5860
+ /** A block placement's framework chrome (P1b+P1c section 8.1). */
5861
+ readonly block: {
5862
+ readonly failed: "This section could not be loaded.";
5863
+ readonly sessionExpired: "Your session has expired. Sign in again to continue.";
5864
+ readonly notImplemented: "This section is not available.";
5865
+ };
5866
+ readonly facts: {
5867
+ readonly empty: "No data";
5868
+ };
5869
+ readonly screen: {
5870
+ readonly failed: "This section could not be loaded.";
5871
+ /** A tab's, step's or collapsed panel's badge: errors, not fields. */
5872
+ readonly errorCount_one: "{{count}} error";
5873
+ readonly errorCount_few: "{{count}} errors";
5874
+ readonly errorCount_many: "{{count}} errors";
5875
+ readonly errorCount_other: "{{count}} errors";
5876
+ readonly expand: "Expand";
5877
+ readonly collapse: "Collapse";
5878
+ };
5879
+ /** A create wizard (a `steps` node in create). */
5880
+ readonly steps: {
5881
+ readonly progress: "Progress";
5882
+ readonly next: "Next";
5883
+ readonly back: "Back";
5884
+ readonly current: "Step {{index}} of {{count}}";
5885
+ };
5886
+ /** A flow's host and outcomes (P4 spec 4.22). */
5887
+ readonly flow: {
5888
+ readonly submit: "Submit";
5889
+ readonly back: "Back";
5890
+ readonly next: "Next";
5891
+ readonly checkAgain: "Check again";
5892
+ readonly close: "Close";
5893
+ readonly resend: "Resend";
5894
+ readonly sendAsNew: "Send as new";
5895
+ readonly sendAsNewConfirm: "This may apply the change twice. Send it as a new request?";
5896
+ readonly startOver: "I've checked — start over";
5897
+ readonly forget: "I've checked — forget it";
5898
+ readonly couldNotCheck: "Couldn't check";
5899
+ readonly couldNotReread: "Couldn't re-read the record. Nothing was sent.";
5900
+ readonly unavailable: "Not allowed.";
5901
+ readonly busyElsewhere: "This action is running in another tab.";
5902
+ readonly notAvailable: "This action is not available";
5903
+ readonly noLongerApplies: "This action no longer applies";
5904
+ readonly discard: "Discard";
5905
+ readonly done: "Done";
5906
+ readonly reset: "Reset";
5907
+ readonly rejected: {
5908
+ readonly validation: "The server refused the values. Nothing was saved.";
5909
+ readonly conflict: "Someone else changed this record. Nothing was saved.";
5910
+ readonly forbidden: "You are not allowed to do this. Nothing was saved.";
5911
+ readonly notFound: "This record is no longer available.";
5912
+ readonly precondition: "The record changed. Nothing was sent.";
5913
+ readonly gone: "This record is no longer available.";
5914
+ };
5915
+ readonly failed: "The request could not be sent. Nothing was saved.";
5916
+ readonly unknown: "The request timed out. It may have been applied. Nothing was resent.";
5917
+ readonly unknownUnhandled: "Check the record before trying again.";
5918
+ readonly resultUnavailable: "Done; the result is not available here.";
5919
+ readonly bulkSummary_one: "{{count}} record processed";
5920
+ readonly bulkSummary_few: "{{count}} records processed";
5921
+ readonly bulkSummary_many: "{{count}} records processed";
5922
+ readonly bulkSummary_other: "{{count}} records processed";
5923
+ readonly bulkLimit_one: "Select at most {{count}} record.";
5924
+ readonly bulkLimit_few: "Select at most {{count}} records.";
5925
+ readonly bulkLimit_many: "Select at most {{count}} records.";
5926
+ readonly bulkLimit_other: "Select at most {{count}} records.";
5927
+ readonly bulkUnresolved: "Skipped: an earlier request for it may have been applied.";
5928
+ readonly bulkNotAttempted: "Not attempted.";
5929
+ };
5930
+ /** A flow's review step (P4 spec 4.4). */
5931
+ readonly review: {
5932
+ readonly changed: "changed";
5933
+ };
4473
5934
  };
4474
5935
  //#endregion
4475
5936
  //#region src/locales/ru.d.ts
@@ -4477,6 +5938,44 @@ declare const ru: {
4477
5938
  readonly validation: {
4478
5939
  readonly requiredHint: "(обязательное)";
4479
5940
  readonly required: "Обязательное поле";
5941
+ readonly string: {
5942
+ readonly minLength: "Слишком короткое значение";
5943
+ readonly maxLength: "Слишком длинное значение";
5944
+ readonly pattern: "Неверный формат";
5945
+ };
5946
+ readonly constraint: {
5947
+ readonly invalid: "Неверное значение";
5948
+ };
5949
+ readonly number: {
5950
+ readonly minimum: "Слишком маленькое значение";
5951
+ readonly maximum: "Слишком большое значение";
5952
+ readonly exclusiveMinimum: "Слишком маленькое значение";
5953
+ readonly exclusiveMaximum: "Слишком большое значение";
5954
+ readonly multipleOf: "Недопустимый шаг значения";
5955
+ };
5956
+ readonly array: {
5957
+ readonly minItems: "Слишком мало элементов";
5958
+ readonly maxItems: "Слишком много элементов";
5959
+ readonly uniqueItems: "Элементы должны быть уникальными";
5960
+ };
5961
+ readonly validator: {
5962
+ readonly invalid: "Неверное значение";
5963
+ readonly exception: "Проверка не пройдена";
5964
+ };
5965
+ readonly schema: {
5966
+ readonly invalid: "Неверное значение";
5967
+ readonly exception: "Проверка не пройдена";
5968
+ };
5969
+ readonly resource: {
5970
+ readonly invalid: "Неверное значение";
5971
+ };
5972
+ readonly type: {
5973
+ readonly string: "Должно быть текстом";
5974
+ readonly number: "Должно быть числом";
5975
+ readonly boolean: "Должно быть «да» или «нет»";
5976
+ readonly array: "Должно быть списком";
5977
+ readonly object: "Должно быть объектом";
5978
+ };
4480
5979
  };
4481
5980
  readonly actions: {
4482
5981
  readonly close: "Закрыть";
@@ -4505,7 +6004,10 @@ declare const ru: {
4505
6004
  };
4506
6005
  readonly confirm: {
4507
6006
  readonly delete: "Удалить запись?";
4508
- readonly deleteMany: "Удалить {{count}} записей?";
6007
+ readonly deleteMany_one: "Удалить {{count}} запись?";
6008
+ readonly deleteMany_few: "Удалить {{count}} записи?";
6009
+ readonly deleteMany_many: "Удалить {{count}} записей?";
6010
+ readonly deleteMany_other: "Удалить {{count}} записи?";
4509
6011
  readonly discardChanges: "Отменить изменения?";
4510
6012
  readonly ok: "ОК";
4511
6013
  readonly confirm: "Подтвердить";
@@ -4515,22 +6017,24 @@ declare const ru: {
4515
6017
  readonly proceed: "Всё равно продолжить";
4516
6018
  readonly back: "Вернуться";
4517
6019
  readonly blocked: "Продолжить нельзя";
4518
- readonly exception: "Проверка не выполнилась";
6020
+ readonly couldNotVerify: "Не удалось проверить «{{checkId}}»";
6021
+ readonly notVerified: "Проверка «{{checkId}}» не выполнилась";
4519
6022
  readonly parseError: "Правило «{{checkId}}» задано неверно: {{detail}}";
4520
6023
  };
4521
6024
  readonly table: {
4522
- readonly selectedCount: "Выбрано: {{count}}";
6025
+ readonly selectedCount_one: "Выбрано: {{count}}";
6026
+ readonly selectedCount_few: "Выбрано: {{count}}";
6027
+ readonly selectedCount_many: "Выбрано: {{count}}";
6028
+ readonly selectedCount_other: "Выбрано: {{count}}";
4523
6029
  readonly empty: "Нет записей";
4524
6030
  readonly loading: "Загрузка…";
4525
- readonly rowsSelected: "Выбрано: {{count}}";
6031
+ readonly rowsSelected_one: "Выбрано: {{count}}";
6032
+ readonly rowsSelected_few: "Выбрано: {{count}}";
6033
+ readonly rowsSelected_many: "Выбрано: {{count}}";
6034
+ readonly rowsSelected_other: "Выбрано: {{count}}";
4526
6035
  readonly selectAllRows: "Выбрать все строки";
4527
6036
  readonly selectRow: "Выбрать строку";
4528
6037
  };
4529
- readonly show: {
4530
- readonly groups: {
4531
- readonly other: "Прочее";
4532
- };
4533
- };
4534
6038
  readonly boolean: {
4535
6039
  readonly yes: "Да";
4536
6040
  readonly no: "Нет";
@@ -4585,7 +6089,10 @@ declare const ru: {
4585
6089
  readonly clear: "Сбросить";
4586
6090
  readonly reset: "Сбросить";
4587
6091
  readonly title: "Фильтры";
4588
- readonly selectedCount: "Выбрано: {{count}}";
6092
+ readonly selectedCount_one: "Выбрано: {{count}}";
6093
+ readonly selectedCount_few: "Выбрано: {{count}}";
6094
+ readonly selectedCount_many: "Выбрано: {{count}}";
6095
+ readonly selectedCount_other: "Выбрано: {{count}}";
4589
6096
  readonly is: "равно";
4590
6097
  readonly isNot: "не равно";
4591
6098
  readonly contains: "содержит";
@@ -4605,11 +6112,12 @@ declare const ru: {
4605
6112
  readonly errors: {
4606
6113
  readonly required: "Обязательное поле";
4607
6114
  readonly invalid: "Неверное значение";
4608
- readonly tooLong: "Не более {{max}} символов";
4609
- readonly tooShort: "Не менее {{min}} символов";
4610
6115
  readonly failed: "Проверка не пройдена";
4611
6116
  readonly validatorFailed: "Не удалось проверить форму. Попробуйте ещё раз.";
4612
6117
  };
6118
+ readonly access: {
6119
+ readonly restricted: "Нет доступа";
6120
+ };
4613
6121
  readonly list: {
4614
6122
  readonly loadingShort: "Загрузка…";
4615
6123
  readonly notRegistered: "Ресурс не зарегистрирован";
@@ -4619,19 +6127,19 @@ declare const ru: {
4619
6127
  readonly errorDetails: "Подробности";
4620
6128
  readonly emptyTitle: "Пока нет записей";
4621
6129
  readonly emptyBody: "Создайте первую запись, чтобы начать.";
6130
+ readonly emptyBodyReadOnly: "Записи появятся здесь, когда их добавят.";
4622
6131
  readonly emptyFilteredTitle: "Ничего не найдено";
4623
6132
  readonly emptyFilteredBody: "Ни одна запись не соответствует текущему фильтру. Сбросьте фильтр, чтобы увидеть все.";
4624
6133
  readonly clearFilter: "Сбросить фильтр";
4625
6134
  readonly create: "+ Создать {{item}}";
4626
6135
  readonly templateUnknown: "Неизвестный";
4627
6136
  readonly search: "Поиск";
6137
+ readonly predicatesDropped: "Некоторые сохранённые фильтры не применены";
6138
+ readonly flowsMenu: "Ещё действия";
4628
6139
  };
4629
- readonly tab: {
4630
- readonly details: "Основное";
4631
- };
4632
- readonly tabBody: {
4633
- readonly empty: "Здесь пока ничего нет.";
4634
- readonly failed: "Не удалось загрузить вкладку.";
6140
+ readonly timeline: {
6141
+ readonly empty: "Нет записей";
6142
+ readonly undated: "Без даты";
4635
6143
  };
4636
6144
  readonly breadcrumbs: {
4637
6145
  readonly create: "Создание";
@@ -4642,10 +6150,25 @@ declare const ru: {
4642
6150
  readonly resources: "Ресурсы";
4643
6151
  readonly main: "Основная навигация";
4644
6152
  readonly breadcrumb: "Хлебные крошки";
4645
- readonly language: "Язык";
4646
6153
  readonly toggleSidebar: "Свернуть меню";
4647
6154
  readonly label: "Навигация";
4648
6155
  };
6156
+ readonly shell: {
6157
+ readonly refresh: "Обновить";
6158
+ readonly help: "Справка";
6159
+ readonly scopeSwitcher: "Переключить";
6160
+ readonly scopePlaceholder: "Выберите…";
6161
+ readonly account: "Учётная запись";
6162
+ };
6163
+ readonly theme: {
6164
+ readonly label: "Тема";
6165
+ readonly light: "Светлая";
6166
+ readonly dark: "Тёмная";
6167
+ readonly system: "Системная";
6168
+ };
6169
+ readonly language: {
6170
+ readonly label: "Язык";
6171
+ };
4649
6172
  readonly pageHeader: {
4650
6173
  readonly backTo: "Назад к списку";
4651
6174
  readonly editTitle: "Изменить {{label}}";
@@ -4657,6 +6180,10 @@ declare const ru: {
4657
6180
  readonly emptyBody: "Для этой страницы не зарегистрировано содержимое.";
4658
6181
  readonly notFoundTitle: "Страница не найдена";
4659
6182
  readonly notFoundBody: "Этот адрес не соответствует ни одной зарегистрированной странице.";
6183
+ readonly recordLoading: "Загрузка…";
6184
+ readonly recordNotFound: "Запись не найдена или у вас нет к ней доступа.";
6185
+ readonly recordSessionExpired: "Сеанс истёк. Войдите снова, чтобы продолжить.";
6186
+ readonly recordError: "Не удалось загрузить запись.";
4660
6187
  };
4661
6188
  readonly notifications: {
4662
6189
  readonly region: "Уведомления";
@@ -4666,6 +6193,12 @@ declare const ru: {
4666
6193
  readonly unsupportedType: "Неподдерживаемый тип";
4667
6194
  readonly unsupportedTypeHint: "Тип «{{type}}» не зарегистрирован для этого ресурса.";
4668
6195
  readonly backToPicker: "Назад к выбору типа";
6196
+ readonly typeOffer: {
6197
+ readonly title: "Выберите тип";
6198
+ readonly hint: "От типа зависит, какие поля есть у записи.";
6199
+ readonly empty: "Здесь нельзя создать запись ни одного типа.";
6200
+ readonly change: "Сменить тип";
6201
+ };
4669
6202
  };
4670
6203
  readonly taskRun: {
4671
6204
  readonly idle: "Не запущено";
@@ -4689,7 +6222,10 @@ declare const ru: {
4689
6222
  };
4690
6223
  readonly toolbar: {
4691
6224
  readonly clearAll: "Очистить всё";
4692
- readonly clearAllAria: "Очистить фильтры: {{count}}";
6225
+ readonly clearAllAria_one: "Очистить фильтры: {{count}}";
6226
+ readonly clearAllAria_few: "Очистить фильтры: {{count}}";
6227
+ readonly clearAllAria_many: "Очистить фильтры: {{count}}";
6228
+ readonly clearAllAria_other: "Очистить фильтры: {{count}}";
4693
6229
  readonly addFilter: "Добавить фильтр";
4694
6230
  readonly filterBy: "Фильтровать по";
4695
6231
  readonly columns: "Колонки";
@@ -4726,6 +6262,7 @@ declare const ru: {
4726
6262
  readonly liveOff: "Авто-обновление: выкл";
4727
6263
  };
4728
6264
  readonly field: {
6265
+ readonly misconfigured: "Это поле настроено неверно";
4729
6266
  readonly removeFile: "Удалить файл";
4730
6267
  readonly pickFile: "Выберите файл или перетащите его сюда";
4731
6268
  readonly dropHint: "Нажмите, чтобы выбрать файл, или перетащите его сюда";
@@ -4737,12 +6274,13 @@ declare const ru: {
4737
6274
  readonly form: {
4738
6275
  readonly systemFields: "Системные поля (только чтение)";
4739
6276
  readonly validating: "Проверка";
4740
- readonly wizardProgress: "Ход заполнения";
4741
6277
  };
4742
6278
  readonly relation: {
4743
6279
  readonly select: "Выберите…";
4744
6280
  readonly failed: "Не удалось загрузить варианты.";
4745
6281
  readonly noResults: "Ничего не найдено.";
6282
+ /** A lazy `by` relation whose key names no row, found when it is clicked (P3). */
6283
+ readonly unavailable: "Недоступно";
4746
6284
  };
4747
6285
  readonly columnMenu: {
4748
6286
  readonly sortSection: "Сортировка";
@@ -4763,13 +6301,23 @@ declare const ru: {
4763
6301
  readonly body: "На этой странице есть несохранённые изменения. Если уйти сейчас, они будут потеряны.";
4764
6302
  readonly stay: "Остаться";
4765
6303
  readonly leave: "Уйти без сохранения";
6304
+ readonly discard: "Отменить изменения";
6305
+ };
6306
+ readonly navigation: {
6307
+ readonly unsavedAction: "В действии на этом экране есть несохранённые данные";
4766
6308
  };
4767
6309
  readonly relationPicker: {
4768
6310
  readonly viewAll: "Открыть список";
4769
6311
  readonly clear: "Очистить";
4770
6312
  readonly advancedSearch: "Расширенный поиск";
4771
6313
  readonly title: "Выбор: {{name}}";
4772
- readonly create: "Создать";
6314
+ readonly view: "Открыть";
6315
+ readonly create: "+ Создать";
6316
+ readonly createdNotFitting: "Запись создана, но не подходит для этого поля";
6317
+ readonly createdNotSelected: "Запись создана, но её не удалось выбрать";
6318
+ readonly chooseFirst: "Сначала выберите {{label}}";
6319
+ readonly cannotSearchBy: "Нельзя искать по {{label}}";
6320
+ readonly notMatching: "Не соответствует текущему фильтру";
4773
6321
  readonly select: "Выбрать";
4774
6322
  readonly prev: "Назад";
4775
6323
  readonly next: "Вперёд";
@@ -4780,10 +6328,20 @@ declare const ru: {
4780
6328
  readonly restorePrompt: "Восстановить?";
4781
6329
  readonly restore: "Восстановить";
4782
6330
  readonly discard: "Отклонить";
6331
+ readonly recordChanged: "Запись изменилась после сохранения черновика";
4783
6332
  readonly timeAgoMoment: "только что";
4784
- readonly timeAgoMinutes: "{{n}} мин назад";
4785
- readonly timeAgoHours: "{{n}} ч назад";
4786
- readonly timeAgoDays: "{{n}} дн назад";
6333
+ readonly timeAgoMinutes_one: "{{count}} минуту назад";
6334
+ readonly timeAgoMinutes_few: "{{count}} минуты назад";
6335
+ readonly timeAgoMinutes_many: "{{count}} минут назад";
6336
+ readonly timeAgoMinutes_other: "{{count}} минуты назад";
6337
+ readonly timeAgoHours_one: "{{count}} час назад";
6338
+ readonly timeAgoHours_few: "{{count}} часа назад";
6339
+ readonly timeAgoHours_many: "{{count}} часов назад";
6340
+ readonly timeAgoHours_other: "{{count}} часа назад";
6341
+ readonly timeAgoDays_one: "{{count}} день назад";
6342
+ readonly timeAgoDays_few: "{{count}} дня назад";
6343
+ readonly timeAgoDays_many: "{{count}} дней назад";
6344
+ readonly timeAgoDays_other: "{{count}} дня назад";
4787
6345
  readonly timeAgoRecently: "недавно";
4788
6346
  };
4789
6347
  readonly commandPalette: {
@@ -4867,13 +6425,19 @@ declare const ru: {
4867
6425
  };
4868
6426
  };
4869
6427
  readonly dialog: {
4870
- readonly titleFilter: "Экспорт {{count}} строк по текущему фильтру";
4871
- /** `estimatedCount` необязателен в `ExportDialogProps` — показывается, когда для экспорта по фильтру нет известной оценки количества строк. */
6428
+ readonly titleFilter_one: "Экспорт {{count}} строки по текущему фильтру";
6429
+ readonly titleFilter_few: "Экспорт {{count}} строк по текущему фильтру";
6430
+ readonly titleFilter_many: "Экспорт {{count}} строк по текущему фильтру";
6431
+ readonly titleFilter_other: "Экспорт {{count}} строки по текущему фильтру";
6432
+ /** `estimatedCount` is optional on `ExportDialogProps` — shown when a filter-scope export has no known row estimate. */
4872
6433
  readonly titleFilterUnknownCount: "Экспорт строк по текущему фильтру";
4873
- readonly titleSelected: "Экспорт {{count}} выбранных строк";
4874
- /** Защитный резерв: Task 8 закрывает триггер по `available` до открытия, так что это достижимо только при прямом/программном монтировании. */
6434
+ readonly titleSelected_one: "Экспорт {{count}} выбранной строки";
6435
+ readonly titleSelected_few: "Экспорт {{count}} выбранных строк";
6436
+ readonly titleSelected_many: "Экспорт {{count}} выбранных строк";
6437
+ readonly titleSelected_other: "Экспорт {{count}} выбранной строки";
6438
+ /** Defensive fallback: Task 8 gates the trigger on `available` before opening, so this is only reachable from a direct/programmatic mount. */
4875
6439
  readonly unavailable: "Экспорт недоступен для этого ресурса.";
4876
- /** Показывается вместо выбора формата, пока загружается отложенная регистрация экспортёра (`ExporterLoader`). */
6440
+ /** Shown in place of the format selector while a deferred exporter registration (`ExporterLoader`) is still loading. */
4877
6441
  readonly loading: "Подготовка экспорта…";
4878
6442
  readonly format: "Формат";
4879
6443
  readonly columns: "Колонки";
@@ -4892,11 +6456,11 @@ declare const ru: {
4892
6456
  readonly continue: "Продолжить в фоне";
4893
6457
  };
4894
6458
  readonly continuesInBackground: "Экспорт продолжается в фоне.";
4895
- /** Кнопки экспорта в тулбаре и в панели массовых действий (Task 8) — одно и то же действие, два места вызова. */
6459
+ /** Toolbar/bulk-bar Export entry points (Task 8). Shared by both — the same action, two scopes. */
4896
6460
  readonly button: {
4897
6461
  readonly label: "Экспорт";
4898
6462
  };
4899
- /** Тосты `useExportRunWatcher()` о завершении экспорта в фоне (Task 8). */
6463
+ /** `useExportRunWatcher()` background-completion toasts (Task 8). */
4900
6464
  readonly toast: {
4901
6465
  readonly succeeded: "Экспорт готов";
4902
6466
  readonly download: "Скачать";
@@ -4906,241 +6470,213 @@ declare const ru: {
4906
6470
  readonly notify: {
4907
6471
  readonly deleted: "Запись удалена";
4908
6472
  };
6473
+ readonly record: {
6474
+ readonly copyId: "Скопировать ID";
6475
+ };
6476
+ /** A quick overlay's header (P3): Back to the previous entry, close, open the record's page. */
6477
+ readonly quick: {
6478
+ readonly back: "Назад к «{{title}}»";
6479
+ readonly close: "Закрыть";
6480
+ readonly openAsPage: "Открыть страницей";
6481
+ };
6482
+ readonly peek: {
6483
+ readonly failed: "Не удалось загрузить";
6484
+ readonly unavailable: "Недоступно";
6485
+ };
6486
+ /** A block placement's framework chrome (P1b+P1c section 8.1). */
6487
+ readonly block: {
6488
+ readonly failed: "Не удалось загрузить раздел.";
6489
+ readonly sessionExpired: "Сессия истекла. Войдите снова, чтобы продолжить.";
6490
+ readonly notImplemented: "Этот раздел недоступен.";
6491
+ };
6492
+ readonly facts: {
6493
+ readonly empty: "Нет данных";
6494
+ };
6495
+ readonly screen: {
6496
+ readonly failed: "Не удалось загрузить раздел.";
6497
+ /** A tab's, step's or collapsed panel's badge: errors, not fields. */
6498
+ readonly errorCount_one: "{{count}} ошибка";
6499
+ readonly errorCount_few: "{{count}} ошибки";
6500
+ readonly errorCount_many: "{{count}} ошибок";
6501
+ readonly errorCount_other: "{{count}} ошибки";
6502
+ readonly expand: "Развернуть";
6503
+ readonly collapse: "Свернуть";
6504
+ };
6505
+ /** A create wizard (a `steps` node in create). */
6506
+ readonly steps: {
6507
+ readonly progress: "Ход заполнения";
6508
+ readonly next: "Далее";
6509
+ readonly back: "Назад";
6510
+ readonly current: "Шаг {{index}} из {{count}}";
6511
+ };
6512
+ /** A flow's host and outcomes (P4 spec 4.22). */
6513
+ readonly flow: {
6514
+ readonly submit: "Отправить";
6515
+ readonly back: "Назад";
6516
+ readonly next: "Далее";
6517
+ readonly checkAgain: "Проверить снова";
6518
+ readonly close: "Закрыть";
6519
+ readonly resend: "Отправить повторно";
6520
+ readonly sendAsNew: "Отправить как новый";
6521
+ readonly sendAsNewConfirm: "Изменение может примениться дважды. Отправить как новый запрос?";
6522
+ readonly startOver: "Я проверил — начать заново";
6523
+ readonly forget: "Я проверил — забыть";
6524
+ readonly couldNotCheck: "Не удалось проверить";
6525
+ readonly couldNotReread: "Не удалось перечитать запись. Ничего не отправлено.";
6526
+ readonly unavailable: "Недоступно.";
6527
+ readonly busyElsewhere: "Это действие выполняется в другой вкладке.";
6528
+ readonly notAvailable: "Это действие недоступно";
6529
+ readonly noLongerApplies: "Это действие больше не применимо";
6530
+ readonly discard: "Отменить";
6531
+ readonly done: "Готово";
6532
+ readonly reset: "Сбросить";
6533
+ readonly rejected: {
6534
+ readonly validation: "Сервер отклонил значения. Ничего не сохранено.";
6535
+ readonly conflict: "Запись изменил кто-то другой. Ничего не сохранено.";
6536
+ readonly forbidden: "У вас нет прав на это действие. Ничего не сохранено.";
6537
+ readonly notFound: "Эта запись больше недоступна.";
6538
+ readonly precondition: "Запись изменилась. Ничего не отправлено.";
6539
+ readonly gone: "Эта запись больше недоступна.";
6540
+ };
6541
+ readonly failed: "Не удалось отправить запрос. Ничего не сохранено.";
6542
+ readonly unknown: "Время ожидания истекло. Изменение могло примениться. Повторно ничего не отправлено.";
6543
+ readonly unknownUnhandled: "Проверьте запись, прежде чем пробовать снова.";
6544
+ readonly resultUnavailable: "Готово; результат здесь недоступен.";
6545
+ readonly bulkSummary_one: "Обработана {{count}} запись";
6546
+ readonly bulkSummary_few: "Обработано {{count}} записи";
6547
+ readonly bulkSummary_many: "Обработано {{count}} записей";
6548
+ readonly bulkSummary_other: "Обработано {{count}} записи";
6549
+ readonly bulkLimit_one: "Выберите не больше {{count}} записи.";
6550
+ readonly bulkLimit_few: "Выберите не больше {{count}} записей.";
6551
+ readonly bulkLimit_many: "Выберите не больше {{count}} записей.";
6552
+ readonly bulkLimit_other: "Выберите не больше {{count}} записей.";
6553
+ readonly bulkUnresolved: "Пропущено: прежний запрос мог уже примениться.";
6554
+ readonly bulkNotAttempted: "Не выполнялось.";
6555
+ };
6556
+ /** A flow's review step (P4 spec 4.4). */
6557
+ readonly review: {
6558
+ readonly changed: "изменено";
6559
+ };
4909
6560
  };
4910
6561
  //#endregion
4911
- //#region src/forms/types.d.ts
4912
- /** The live values map a declarative form reads from and writes to. */
4913
- type FormValues = Record<string, unknown>;
4914
- /**
4915
- * A friendly validator return, resolved through `normalizeFieldIssues` at
4916
- * the boundary. Reused for both per-field validators (`ResourceFieldDefinition.
4917
- * validators`, from `@burojs/core`) and the form-level `validate` below.
4918
- */
4919
- type FormLevelValidationResult = FieldIssue | readonly FieldIssue[] | null | undefined;
6562
+ //#region src/forms/controller/screen-state.d.ts
4920
6563
  /**
4921
- * Cross-field / whole-form validator. Receives the full values map plus the
4922
- * same `{ values, field? }` context shape core's per-field `FieldValidator`
4923
- * uses — deliberately NOT the richer visibility/permission `FieldCtx` from
4924
- * the design doc's §4, since predicates and conditional visibility are
4925
- * Task 4. Keeping this context-free is what lets `renderHook` run this
4926
- * controller with no provider wrapper.
6564
+ * Which panels of a screen are open: the `open` input of `planScreen` (P1b+P1c
6565
+ * decision 13), plus `expanded`: the `collapse: 'closed'` panels a user opened.
6566
+ * A host holds no screen, so it cannot list the panels a screen starts
6567
+ * collapsed; the controller adds every `collapse: 'closed'` panel that is not
6568
+ * in `expanded` to `collapsed` before it plans.
4927
6569
  */
4928
- type FormValidate = (values: FormValues, ctx: ValidationContext) => FormLevelValidationResult | Promise<FormLevelValidationResult>;
4929
- /** A validation issue projected for display: one field, one message. */
4930
- interface ResolvedIssue {
4931
- readonly field: string;
4932
- readonly severity: ValidationIssueSeverity;
4933
- readonly message: MessageReference;
6570
+ type ScreenOpenState = NonNullable<PlanScreenInput['open']> & {
6571
+ readonly expanded?: readonly string[];
6572
+ };
6573
+ interface ScreenStateValue {
6574
+ readonly open: ScreenOpenState;
6575
+ openPanel(containerId: string, panelKey: string): void;
6576
+ setCollapsed(containerId: string, collapsed: boolean): void;
6577
+ /** The inline flows this screen instance holds (P6 3.12); released when the screen unmounts. */
6578
+ readonly inline: InlineFlowHolder;
6579
+ }
6580
+ interface ScreenStateProviderProps {
6581
+ /** The uncontrolled start value. */
6582
+ readonly initialOpen?: ScreenOpenState;
4934
6583
  /**
4935
- * Interpolation params for a bundle-translated `message.key` (e.g. a
4936
- * check's `{ checkId, show, trigger }` — `evaluateChecks`, `@burojs/core`).
4937
- * Forwarded from the source `ValidationIssue.params` so a renderer's
4938
- * `t(message.key, { params })` call can fill in a `{{placeholder}}` in a
4939
- * registered bundle entry, mirroring `resource-form.tsx`'s
4940
- * `resolveIssueMessage` and `<CheckConfirmDialog>`'s own `issueText`.
6584
+ * Controlled: when given, the provider holds no state of its own and plans
6585
+ * this value, so a host can drive it after mount (URL tab state reaching
6586
+ * the plan).
4941
6587
  */
4942
- readonly params?: Readonly<Record<string, unknown>>;
6588
+ readonly open?: ScreenOpenState;
6589
+ /** Called by `openPanel` and `setCollapsed`, controlled or not. */
6590
+ readonly onOpenChange?: (next: ScreenOpenState) => void;
6591
+ readonly children: ReactNode;
4943
6592
  }
4944
6593
  /**
4945
- * Minimal event shape `handleSubmit`'s returned handler needs. Structurally
4946
- * compatible with a real DOM `FormEvent`, but the controller itself never
4947
- * imports React's DOM event types — it stays usable outside a `<form>`.
6594
+ * Holds what is open on a screen (tabs, steps, collapsed panels) for the form
6595
+ * controller and the renderer below it. Controlled through `open` /
6596
+ * `onOpenChange`, or uncontrolled from `initialOpen`.
6597
+ */
6598
+ declare function ScreenStateProvider({ initialOpen, open, onOpenChange, children }: ScreenStateProviderProps): ReactElement;
6599
+ /** The screen state, or `null` outside a provider: the controller then plans without `open`. */
6600
+ declare function useScreenState(): ScreenStateValue | null;
6601
+ //#endregion
6602
+ //#region src/forms/controller/use-form-controller.d.ts
6603
+ /**
6604
+ * The form controller (P1b+P1c section 10.3): one planned screen, its values
6605
+ * with their seed provenance, validation, checks and the payload allow-list.
6606
+ * A record form passes `resource` and `mode`, and its screen is read from the
6607
+ * compiled snapshot, never from a handle; a resource-less form passes
6608
+ * `fields` (and a `body`), and its screen is compiled by `compileFormScreen`.
4948
6609
  */
4949
- interface SubmitEventLike {
4950
- readonly preventDefault?: () => void;
6610
+ declare function useFormController<F extends ResourceFieldMap>(options: UseFormControllerFormOptions<F>): FormController;
6611
+ declare function useFormController(options: UseFormControllerRecordOptions): FormController;
6612
+ //#endregion
6613
+ //#region src/forms/validation-message.d.ts
6614
+ /** What {@link resolveValidationMessage} reads besides the issue. */
6615
+ interface ValidationMessageContext {
6616
+ /** A translator that returns the key itself on a miss (`useTranslate`, `useT`). */
6617
+ readonly t: (key: string, options?: {
6618
+ params?: Record<string, unknown>;
6619
+ }) => string;
6620
+ /** `resources.<r>.fields.<f>` (a flow form's: `resources.<r>.actions.<name>.fields.<f>`); absent for a form-level issue. */
6621
+ readonly fieldKey?: string;
4951
6622
  }
4952
6623
  /**
4953
- * The prop bag `<Field {...form.field(name)} />` spreads, and the raw escape
4954
- * hatch for a hand-rolled input. Carries `resource` (derived from
4955
- * `i18nPrefix` via `i18nPrefixToRef`) but deliberately NOT a UI `fallback`
4956
- * node — `@burojs/react` has no shadcn/DOM dependency; the `<Field>` brick
4957
- * in `@burojs/ui-shadcn` supplies its own default fallback.
6624
+ * The one text of a validation issue, resolved at render so a locale switch
6625
+ * reaches it. A lookup hits when `t` returns something other than the key:
6626
+ *
6627
+ * 1. `${fieldKey}.validation.${code}` — the author's per-field override;
6628
+ * 2. `message.key`, when it is not `validation.${code}` (a server or semantic key);
6629
+ * 3. `message.default` — the schema's, validator's or server's own text;
6630
+ * 4. `validation.${code}` — the framework's text for a built-in code;
6631
+ * 5. `errors.failed`.
4958
6632
  *
4959
- * `required`/`disabled`/`hidden` are RESOLVED (Task 4): `hidden` is
4960
- * `!evaluateVisible(field.visible, ctx, values)` (`@burojs/core`'s existing
4961
- * `visible` convention, reused as-is); `required`/`disabled` are each
4962
- * either a static boolean or a predicate `(ctx, values) => boolean`
4963
- * evaluated the same way — see `packages/react/src/forms/conditional.ts`.
6633
+ * Built-in constraint issues carry no `default`, so step 4 answers them in
6634
+ * every locale; an issue with its own `default` keeps it unless step 1 hits.
4964
6635
  */
4965
- interface FieldBinding {
4966
- readonly name: string;
4967
- readonly value: unknown;
4968
- readonly onChange: (value: unknown) => void;
4969
- readonly onBlur: () => void;
4970
- readonly error?: ResolvedIssue;
4971
- readonly warnings: readonly ResolvedIssue[];
4972
- readonly required: boolean;
4973
- readonly disabled: boolean;
4974
- readonly hidden: boolean;
4975
- readonly resource: EntityRef;
4976
- readonly field: ResourceFieldDefinition;
4977
- /**
4978
- * A relation field's scope, RESOLVED against the current `values` snapshot
4979
- * (design doc §5). `undefined` when the field declared no `filter` opt (or
4980
- * a function-form `filter` threw / returned a non-array — same
4981
- * "broken predicate behaves as if never declared" convention
4982
- * `resolvePredicate` uses for `required`/`disabled`, see
4983
- * `./conditional.js`). `@burojs/ui-shadcn`'s `<Field>` merges this into
4984
- * the field's `meta.filters` — the channel `RelationPickerInput` already
4985
- * reads (`relation-picker.tsx:478-487,526`) — so a plain `{...binding}`
4986
- * spread onto `<Field>` (what `<DeclarativeForm>` already does) needs no
4987
- * extra wiring per field.
4988
- *
4989
- * Typed as `FixedFilterInput` (not the bare AST) so it widens together
4990
- * with `RelationPickerProps.fixedFilters` and
4991
- * `RelationPickerDialogProps.fixedFilters` downstream — a bare expression
4992
- * is still a valid `FixedFilterInput`, so `resolveFieldFilter`'s bare-array
4993
- * return (below) keeps satisfying this without a cast.
4994
- */
4995
- readonly fixedFilters?: readonly FixedFilterInput[];
4996
- }
6636
+ declare function resolveValidationMessage(issue: Pick<ValidationIssue, 'code' | 'message' | 'params'>, context: ValidationMessageContext): string;
6637
+ //#endregion
6638
+ //#region src/hooks/use-current-user-id.d.ts
4997
6639
  /**
4998
- * A relation field's `filter` opt (design doc §5) — static, or resolved
4999
- * per-render against the current form values. NOT a typed
5000
- * `@burojs/field-types` builder opt (`RelationFieldOpts` carries no `filter`
5001
- * key — verified empirically the same way the Task 4 report verified
5002
- * `disabled`/`clearWhenHidden`); reachable via the field's raw `.filter` or
5003
- * the builder's `.opts.filter` — see `readFieldFilterOpt` in
5004
- * `./conditional.js`.
5005
- */
5006
- type RelationFilterOpt = readonly FilterExpression<ErasedResourceHandle>[] | ((values: FormValues) => readonly FilterExpression<ErasedResourceHandle>[]);
5007
- interface UseDeclarativeFormOptions {
5008
- /** The existing field-def map — the same shape `defineResource`'s `f` builder produces. */
5009
- readonly fields: ResourceFieldMap;
5010
- /** Controlled values. Presence (not truthiness) of this key selects controlled mode. */
5011
- readonly value?: FormValues;
5012
- /** Uncontrolled seed. Ignored once `value` is provided. */
5013
- readonly defaultValue?: FormValues;
5014
- readonly onChange?: (values: FormValues) => void;
5015
- /** Drives label/hint/placeholder i18n, doc address, and the `<Field>` EntityRef. */
5016
- readonly i18nPrefix?: string;
5017
- /** Optional whole-form Standard Schema. */
5018
- readonly schema?: StandardSchemaV1;
5019
- /** Cross-field validation; may return warnings as well as errors. */
5020
- readonly validate?: FormValidate;
5021
- /**
5022
- * Optional per-field capability seam. Declared for forward compatibility
5023
- * with the design doc's §4 shape; still unread — an OPEN QUESTION (design
5024
- * doc §13) whether it should be shaped now to also serve a later
5025
- * `<ResourceForm>` migration, or stay minimal. NOT how `field(name)
5026
- * .disabled` is resolved — that is the new `disabled` predicate opt
5027
- * (Task 4, `./conditional.js`), a separate channel from this capability
5028
- * seam.
5029
- */
5030
- readonly editable?: (field: string, ctx: ValidationContext) => boolean;
5031
- readonly onSubmit?: (values: FormValues) => unknown | Promise<unknown>;
5032
- /**
5033
- * Conditional checks for this form. Declarative forms are resource-less, so
5034
- * checks are supplied here rather than resolved from a registry. Warnings
5035
- * never gate `canSubmit`; a `severity: 'error'` check does.
5036
- */
5037
- readonly checks?: readonly Check[];
5038
- /**
5039
- * Explicit `on: ['create' | 'edit' | ...]` surface for the check pass.
5040
- * When omitted, `handleSubmit`/`revalidate` INFER it from whether a
5041
- * baseline was supplied (`value`/`defaultValue` present → `'edit'`, absent
5042
- * → `'create'`) — but that heuristic misfires for the ordinary "seed a
5043
- * new record's defaults" create-form shape
5044
- * (`<DeclarativeForm defaultValue={{ status: 'draft' }} />`), which DOES
5045
- * supply `defaultValue` yet is still a create. Set this explicitly
5046
- * whenever the form's create/edit-ness can't be read off "was a baseline
5047
- * given" — the inference stays the default because the brief mandates it
5048
- * as the fallback, not because it's always right.
5049
- */
5050
- readonly surface?: CheckSurface;
5051
- /**
5052
- * Called by `handleSubmit` when a submit-time check pass produced
5053
- * `confirmable` issues but no `blockers` — ordinary validation AND checks
5054
- * both passed, and there is something worth a second look before saving.
5055
- * The host (`<DeclarativeForm>`) supplies this to drive its own confirm
5056
- * gate (built around `<CheckConfirmDialog>`), seeded with the exact
5057
- * `CheckOutcome` this controller already computed. There is deliberately
5058
- * no `useCheckGate(resource)`-style re-derivation here: that hook resolves
5059
- * its check list from `app.checkSource.checks(resource)`, which has
5060
- * nothing to key off for a resource-less form whose checks arrive as a
5061
- * plain option. Resolving `'cancel'` aborts the submit (`onSubmit` is
5062
- * never called); resolving `'proceed'` — or omitting this option
5063
- * entirely — behaves exactly like an ordinary non-blocking warning, and
5064
- * the submit proceeds.
5065
- */
5066
- readonly onConfirmChecks?: (outcome: CheckOutcome) => Promise<'proceed' | 'cancel'>;
5067
- }
5068
- interface DeclarativeFormApi {
5069
- /** Spread into `<Field/>`, or read raw for a hand-rolled input. */
5070
- field(name: string): FieldBinding;
5071
- /** Visible fields only, in `options.fields` declaration order — a hidden field is excluded (Task 4). */
5072
- readonly fields: readonly FieldBinding[];
5073
- readonly values: FormValues;
5074
- /** severity 'error' issues, form-level plus per-field, from the last validation pass. */
5075
- readonly errors: readonly ResolvedIssue[];
5076
- /** severity 'warning' | 'info' issues — never block `canSubmit`. */
5077
- readonly warnings: readonly ResolvedIssue[];
5078
- /** `errors.length === 0 && !isSubmitting`. */
5079
- readonly canSubmit: boolean;
5080
- /** Current `values` vs. the baseline captured once at mount. */
5081
- readonly isDirty: boolean;
5082
- readonly isSubmitting: boolean;
5083
- /** The last check pass, for a host that mounts its own confirm gate. */
5084
- readonly checkOutcome: CheckOutcome | null;
5085
- handleSubmit(cb?: (values: FormValues) => unknown | Promise<unknown>): (e?: SubmitEventLike) => Promise<void>;
5086
- setValue(name: string, value: unknown): void;
5087
- reset(next?: FormValues): void;
5088
- /**
5089
- * Re-run every field's + the form's validation against the current
5090
- * `values` and update `errors`/`warnings`. A live seam for a renderer or
5091
- * adopter to call on value change (e.g. debounced) — this controller does
5092
- * NOT call it itself; deciding when to revalidate outside of blur/submit
5093
- * is the caller's job.
5094
- */
5095
- revalidate(): Promise<void>;
5096
- }
6640
+ * The signed-in user's id from the `user` context slot: a non-empty string,
6641
+ * or a finite number as a string; `null` without a slot, while the slot is
6642
+ * pending and for an empty id. The one user-id read of the framework (drafts,
6643
+ * sign-out, recent records).
6644
+ */
6645
+ declare function useCurrentUserId(): string | null;
5097
6646
  //#endregion
5098
- //#region src/forms/use-declarative-form.d.ts
6647
+ //#region src/hooks/url-state/use-search-params-writer.d.ts
5099
6648
  /**
5100
- * Headless controller for a declarative form: values (controlled or
5101
- * uncontrolled), per-field + cross-field + whole-form-schema validation, the
5102
- * error/warning partition, `canSubmit`, `isDirty`, `handleSubmit` with an
5103
- * anti-double-submit latch, conditional visibility, predicate `required`/
5104
- * `disabled`, and `clearWhenHidden`.
5105
- *
5106
- * Reads `useOptionalVisibilityCtx()` (`../hooks/use-permissions.js`) as the
5107
- * `ctx` argument every `visible`/`required`/`disabled` predicate receives —
5108
- * NOT `useVisibilityCtx`, which throws outside a `BuroProvider`. This
5109
- * controller must stay instantiable via a bare
5110
- * `renderHook(() => useDeclarativeForm(...))`, no wrapper (Task 3's own
5111
- * suite does this, and a form with no conditional fields has no reason to
5112
- * require the whole app's provider tree just to mount) — the optional
5113
- * variant defaults `ctx` to `{}` outside a provider instead.
6649
+ * Writes in-page URL state (a record tab, list state): `update` edits a copy
6650
+ * of the current search params, and the result is written on the same
6651
+ * pathname, by default with `replace`. Never asks the navigation guard and
6652
+ * never changes the pathname; a navigation to another page goes through
6653
+ * `useRouteNavigate`. Without a router adapter it does nothing.
5114
6654
  */
5115
- declare function useDeclarativeForm(options: UseDeclarativeFormOptions): DeclarativeFormApi;
6655
+ declare function useSearchParamsWriter(): (update: (params: URLSearchParams) => void, options?: {
6656
+ readonly history?: 'push' | 'replace';
6657
+ }) => void;
5116
6658
  //#endregion
5117
- //#region src/forms/conditional.d.ts
5118
- /** A field's resolved (predicate-evaluated) visibility/required/disabled state for one values snapshot. */
5119
- interface ResolvedFieldFlags {
5120
- /** `!visible` — the `fields` getter and validation both key off this. */
5121
- readonly hidden: boolean;
5122
- readonly required: boolean;
5123
- readonly disabled: boolean;
6659
+ //#region src/record/record-frame-context.d.ts
6660
+ /** What a record frame gives the pieces rendered inside `<RecordScreen>`. */
6661
+ interface RecordFrameValue {
6662
+ /** The resource's canonical name. */
6663
+ readonly resource: string;
6664
+ readonly recordId: string | undefined;
6665
+ readonly mode: ScreenMode;
6666
+ /** The record being shown or edited, once it is read; `undefined` in create. */
6667
+ readonly record: Readonly<Record<string, unknown>> | undefined;
6668
+ /** The form controller planning the frame's screen. */
6669
+ readonly controller: FormController;
5124
6670
  }
6671
+ /** The record frame `<RecordScreen>` provides; `null` outside one. */
6672
+ declare const RecordFrameContext: import("react").Context<RecordFrameValue | null>;
5125
6673
  /**
5126
- * Resolve one field's `hidden`/`required`/`disabled` against a ctx + values
5127
- * snapshot.
5128
- *
5129
- * `hidden` is `!evaluateVisible(...)` — core's real evaluator
5130
- * (`@burojs/core`'s `permissions.ts:121-136`), reused as-is rather than
5131
- * reimplemented: undefined → visible (adopters opt INTO gating); a throw →
5132
- * NOT visible, unconditionally (fail-closed). This is THE existing `visible`
5133
- * convention every other renderer (`<ResourceList>`, `<ResourceShow>`,
5134
- * `<ResourceForm>`) already consults — reusing it, rather than routing
5135
- * `visible` through the more generic `resolvePredicate` below, is what
5136
- * keeps this controller's conditional-visibility semantics identical to
5137
- * theirs.
5138
- *
5139
- * `required`/`disabled` default to `false` when undeclared or on a throw
5140
- * (`resolvePredicate`) — "no predicate declared → behave exactly as before
5141
- * this feature existed".
6674
+ * The record frame around the caller. A frame piece rendered outside
6675
+ * `<RecordScreen>` is a development error (P1b+P1c decision 6), so this throws there.
5142
6676
  */
5143
- declare function resolveFieldFlags(fieldDef: unknown, ctx: unknown, values: FormValues): ResolvedFieldFlags;
6677
+ declare function useRecordFrame(): RecordFrameValue;
6678
+ /** The record frame around the caller, or `null` outside `<RecordScreen>`. */
6679
+ declare function useOptionalRecordFrame(): RecordFrameValue | null;
5144
6680
  //#endregion
5145
6681
  //#region src/export/runs-store.d.ts
5146
6682
  interface ExportRunEntry {
@@ -5286,5 +6822,5 @@ declare function useResourceExport(resource: string, opts?: UseResourceExportOpt
5286
6822
  declare const PACKAGE_NAME: "@burojs/react";
5287
6823
  declare const PACKAGE_VERSION: "0.0.0";
5288
6824
  //#endregion
5289
- export { type AppActionEntry, type ApplyViewToUrlStateOptions, type ApplyViewUrlStateSetters, type AuthController, type BuildFormDiffArgs, type BuroDehydratedData, BuroPresentationOutlet, BuroProvider, type BuroProviderProps, BuroRoutes, BuroRuntimeProvider, type BuroRuntimeProviderProps, type CapabilityResolver, type CheckGateRequest, type CheckGateState, type CodecOptions, type CollectionDisplayProps, type CollectionInputProps, ComponentsContext, type CountExecution, type CreateAuthControllerOptions, type CreateDataRuntimeOptions, type CreateDataRuntimeTransitionParticipantOptions, type CustomPageRouteDescriptor, DEFAULT_HISTORY_MODE, type DataRuntimeContextValue, type DataRuntimeLease, type DataRuntimeTransitionParticipant, type DataSessionController, type DataSessionState, type DataSessionTransitionParticipant, type DeclarativeFormApi, type DefinedField, type DocPanelApi, type DocPanelTarget, type DocScopeView, type DocTopicView, DocsProvider, type DocumentedScope, EMPTY_FIELD_IMPLEMENTATIONS, type ExportRunEntry, ExportRunsStore, type FieldAccessibilityIds, type FieldBinding, type FieldI18n, type FieldImplementation, type FieldImplementationCatalog, type FieldImplementationComponent, type FieldImplementationDiagnostic, type FieldImplementationLayout, type FieldImplementationMap, FieldImplementationProvider, type FieldImplementationSource, type FieldPresentation, type FieldRenderContext, type FieldRenderSurface, type FieldTestIds, type FieldType, type FieldUiHints, type FileItem, type FileItemStatus, type FilterFieldSpec, type FilterFieldsResult, type FilterKind, type FormSubmitAudit, type FormSubmitAuditArgs, type FormValues, type HistoryMode, I18nContext, type InfiniteListData, type InitialManifest, LOCAL_USER_SCOPE_ID, type LocalStorageUiSettingsAdapterOptions, type ManifestHydration, type ManifestHydrationScope, type NativeInfiniteListResult, type NativeMutationOptions, type NativeQueryOptions, type NativeQueryResult, PACKAGE_NAME, PACKAGE_VERSION, type PresentationsMode, RENDER_TIME_DATA, RESOLVE_COOLDOWN_MS, RESOURCE_URL_STATE_LIMITS, type RecentEnvelope, type RecentRecord, RecordCodecError, type RecordCodecOutcome, type RelatedRecord, RelationAggregatorProvider, type RelationAggregatorProviderProps, type RelationFilterOpt, type ResolvedFieldFlags, type ResolvedFieldImplementation, type ResolvedIssue, type ResolvedItemAction, ResourceContext, type ResourceListController, ResourceListProvider, type ResourceListProviderProps, type ResourceRecordCodec, type ResourceRouteDescriptor, type ResourceUrlState, type RouteDescriptor, type RouterAdapter, RouterAdapterProvider, type RouterAdapterProviderProps, type RowRecord, type SavedViewEntry, type ScalarDisplayProps, type ScalarInputProps, type SetterOptions, type TaskRunController, type TaskRunReporter, type TestIdBuilder, TestIdContext, UIActivationSurfaceProvider, type UIInstance, UIInstanceProvider, type UIPresent, type UIPresentInput, type UIPresentationHost, type UIPresentationHostProps, type UIPresentationHosts, type UIPresentationLifecycle, type UIPresentationOptionsSchema, UIPresentationRuntimeProvider, UIPresentations, type UIPresentationsProps, UiSettingsProvider, type UiSettingsProviderProps, type UrlCache, type UseActionArgs, type UseActionResult, type UseBulkActionArgs, type UseBulkActionResult, type UseCapabilityArgs, type UseCheckGateResult, type UseChecksOptions, type UseChecksResult, type UseCommandPaletteOptions, type UseCommandPaletteResult, type UseCountOptions, type UseCreateManyOptions, type UseCreateOneOptions, type UseCustomMutationOptions, type UseCustomQueryOptions, type UseDeclarativeFormOptions, type UseDeleteManyOptions, type UseDeleteOneOptions, type UseEnumValuesResult, type UseFieldImplementationArgs, type UseFileFieldArgs, type UseFileFieldResult, type UseFileUrlsOpts, type UseFileUrlsResult, type UseFilterFieldsOptions, type UseInfiniteListOptions, type UseListOptions, type UseManyOptions, type UseOneOptions, type UseRelatedRecordResult, type UseResourceExportOptions, type UseResourceExportResult, type UseResourceListOptions, type UseSavedViewsResult, type UseTaskRunOptions, type UseTrackRecentRecordOptions, type UseUiSettingsCollectionResult, type UseUiSettingsResult, type UseUiSettingsSingletonResult, type UseUpdateManyOptions, type UseUpdateOneOptions, applyViewToUrlState, buildDI, buildFormDiff, buroMutationOptions, clearRecentRecords, composeFieldImplementations, createAuthController, createDataRuntime, createDataRuntimeTransitionParticipant, createDataSession, createLocalStorageUiSettingsAdapter, createManifestHydrationScope, createResourceRecordCodec, createRuntimeActionDataAccess, createUrlCache, defineFieldWithImplementation, dehydrateBuroData, deserializeResourceState, disposeBuroRequest, en, formatBytes, generateRouteDescriptors, getRecentRecords, getResourceParams, hydrateBuroData, hydrateManifest, isCustomPageRoute, matchesAccept, mergeComponents, modeForSurface, normalizeServerRecordIssues, resolveCountExecution, resolveFieldFlags, resolveFieldImplementation, ru, serializeBuroHydration, serializeResourceState, sharedUrlCache, useAction, useApp, useAppActions, useApplicationSnapshot, useAudit, useAvailability, useAvailabilityGate, useAvailabilityResolver, useBulkAction, useCapability, useCapabilityResolver, useChangeLocale, useCheckGate, useChecks, useCommandPalette, useComponent, useCount, useCreateMany, useCreateOne, useCtx, useCurrentResource, useCurrentUIActivation, useCustomMutation, useCustomQuery, useDataRuntime, useDebouncedValue, useDeclarativeForm, useDefaultComponent, useDeleteMany, useDeleteOne, useDismissExportRun, useDoc, useDocPanel, useDocScopes, useDocTopic, useDocTopics, useDocTrailScopes, useDocsConfig, useDocumentedScopes, useEnumValues, useExportRuns, useFieldImplementation, useFieldImplementationCatalog, useFileField, useFileStorage, useFileUrls, useFilterFields, useFormSubmitAudit, useHasDocTopics, useI18nCaps, useIdentity, useInfiniteList, useIsAuthenticated, useItemActions, useLayerRuntime, useLayerStatus, useList, useLocalStorage, useLocale, useLogin, useLogout, useMany, useOne, useOptionalDataRuntime, useOptionalFileStorage, useOptionalLocale, useOptionalT, useOptionalUIActivationSurface, useOptionalUIInstance, useOptionalUIPresentation, useOptionalVisibilityCtx, usePermissions, useProjection, usePublishDocVariant, useRecordTitle, useRelatedRecord, useResource, useResourceExport, useResourceI18n, useResourceList, useResourceListContext, useResourceUrlState, useRouterAdapter, useSavedViews, useScopeCtx, useSetSlice, useT, useTaskRun, useTestIds, useTrackRecentRecord, useTrailHasDocs, useTranslate, useUIInstance, useUIPresent, useUIPresentationRuntime, useUiSettings, useUpdateMany, useUpdateOne, useVisibilityCtx, useVisibleTemplates, validateBuroHydration };
6825
+ export { type AppActionEntry, type AppAudit, type ApplyViewToUrlStateOptions, type ApplyViewUrlStateSetters, type AuthController, BLOCK_COMPONENT, type BlockDataEntryName, type BlockImplementation, type BlockImplementationCatalog, type BlockScreenProps, type BlockViewPropValues, type BlockViewProps, type BuroDehydratedData, BuroPresentationOutlet, BuroProvider, type BuroProviderProps, BuroRoutes, BuroRuntimeProvider, type BuroRuntimeProviderProps, type CandidatesAccess, type CapabilityResolver, type CheckGateRequest, type CheckGateState, type CodecOptions, type CollectionDisplayProps, type CollectionInputProps, ComponentsContext, type CountExecution, type CreateAuthControllerOptions, type CreateDataRuntimeOptions, type CreateDataRuntimeTransitionParticipantOptions, type CustomPageRouteDescriptor, DEFAULT_HISTORY_MODE, DEFAULT_LANGUAGE_STORAGE_KEY, type DataRuntimeContextValue, type DataRuntimeLease, type DataRuntimeTransitionParticipant, type DataSessionController, type DataSessionState, type DataSessionTransitionParticipant, type DefinedField, type DismissGuard, DismissGuardScope, type DismissScopeValue, DispatcherProvider, type DispatcherProviderProps, type DocPanelApi, type DocPanelTarget, type DocScopeView, type DocTopicView, DocsProvider, type DocumentedScope, EMPTY_FIELD_IMPLEMENTATIONS, type EntryAnswer, type EntryKind, type ExportRunEntry, ExportRunsStore, FLOW_COMPONENT, type FieldAccessibilityIds, type FieldBinding, type FieldI18n, type FieldImplementation, type FieldImplementationCatalog, type FieldImplementationComponent, type FieldImplementationDiagnostic, type FieldImplementationLayout, type FieldImplementationMap, FieldImplementationProvider, type FieldImplementationSource, type FieldMetaState, type FieldPresentation, type FieldRenderContext, type FieldRenderSurface, type FieldTestIds, type FieldType, type FieldUiHints, type FileItem, type FileItemStatus, type FilterFieldSpec, type FilterFieldsResult, type FilterKind, FlowRowBatchContext, type FlowRowBatchValue, type FlowRowControl, type FlowRowState, type FlowScreenProps, type FlowStart, type FormController, type FormControllerFailure, type FormDraftState, type FormSteps, type FormSubmitResult, type FormValues, FrameCheckContext, type FrameCheckValue, type FrameImplementation, type FrameImplementationCatalog, type FrameNodeAttributes, type FramePiece, type FrameStep, type FrameTab, type FrameTranslate, type FrameViewProps, type HistoryMode, type HostPage, I18nContext, type InfiniteListData, type InitialManifest, type InlineFlowHolder, type InlineLease, LOCAL_USER_SCOPE_ID, type ListFrameViewProps, type ListState, type LocalStorageUiSettingsAdapterOptions, type ManifestHydration, type ManifestHydrationScope, type NativeInfiniteListResult, type NativeMutationOptions, type NativeQueryOptions, type NativeQueryResult, type NavigationBlockerHandle, type NavigationGuardPrompt, type NavigationGuardText, type NotificationSink, type NotificationSinkMessage, PACKAGE_NAME, PACKAGE_VERSION, PEEK_COMPONENT, type PageAccess, type PageFlowGate, type PageFrameViewProps, type PageScreen, type PanelFrameViewProps, type PeekFrameViewProps, type PeekScreenProps, type PlacementRuntime, type PlacementView, type PresentBlockInput, type PresentFlowInput, type PresentationsMode, type ProjectedRecord, QUICK_ANCHOR_ATTRIBUTE, QUICK_COMPONENT, type QuickApi, type QuickEntry, QuickEntryContext, type QuickEntryValue, type QuickOpen, type QuickScreenProps, type QuickSettledRow, type QuickStack, RELATION_BATCH_MAX_PAGE, RENDER_TIME_DATA, RESERVED_LIST_SUFFIXES, RESOLVE_COOLDOWN_MS, RESOURCE_URL_STATE_LIMITS, RecordCodecError, type RecordCodecOutcome, RecordFrameContext, type RecordFrameSlots, type RecordFrameValue, type RecordFrameViewProps, type RelatedRecord, type RelationFilterOpt, type ResolvedFieldImplementation, type ResolvedIssue, type ResolvedItemAction, ResourceContext, type ResourceFlowSurface, type ResourceListController, ResourceListProvider, type ResourceListProviderProps, type ResourceRecordCodec, type ResourceRouteDescriptor, type ResourceUrlState, type RouteDescriptor, type RouteNavigate, type RouteNavigateOptions, RouteSourceContext, type RouterAdapter, RouterAdapterProvider, type RouterAdapterProviderProps, type RowRecord, type SavedViewEntry, type ScalarDisplayProps, type ScalarInputProps, type ScreenOpenState, ScreenStateProvider, type ScreenStateProviderProps, type ScreenStateValue, type SetterOptions, type StepsFrameViewProps, type TabsFrameViewProps, type TaskRunController, type TaskRunReporter, type TestIdBuilder, TestIdContext, UIActivationSurfaceProvider, type UIInstance, UIInstanceProvider, type UIPresent, type UIPresentInput, type UIPresentationHost, type UIPresentationHostProps, type UIPresentationHosts, type UIPresentationLifecycle, type UIPresentationOptionsSchema, UIPresentationRuntimeProvider, type UIPresentationScreens, UIPresentations, type UIPresentationsProps, UiSettingsProvider, type UiSettingsProviderProps, type UrlCache, type UseCapabilityArgs, type UseCheckGateResult, type UseChecksOptions, type UseChecksResult, type UseCommandPaletteOptions, type UseCommandPaletteResult, type UseCountOptions, type UseCreateOneOptions, type UseCustomMutationOptions, type UseCustomQueryOptions, type UseDeleteManyOptions, type UseDeleteOneOptions, type UseEnumValuesResult, type UseFieldImplementationArgs, type UseFileFieldArgs, type UseFileFieldResult, type UseFileUrlsOpts, type UseFileUrlsResult, type UseFilterFieldsOptions, type UseFormControllerFormOptions, type UseFormControllerRecordOptions, type UseInfiniteListOptions, type UseListOptions, type UseManyOptions, type UseOneOptions, type UseRefreshResult, type UseRelatedRecordOptions, type UseRelatedRecordResult, type UseResourceExportOptions, type UseResourceExportResult, type UseResourceListOptions, type UseSavedViewsResult, type UseTaskRunOptions, type UseUiSettingsCollectionResult, type UseUiSettingsResult, type UseUiSettingsSingletonResult, type UseUpdateManyOptions, type UseUpdateOneOptions, type ValidationMessageContext, applyViewToUrlState, buildDI, buroMutationOptions, composeBlockImplementations, composeFieldImplementations, composeFrameImplementations, conjoinHostPage, createAuthController, createDataRuntime, createDataRuntimeTransitionParticipant, createDataSession, createLocalStorageUiSettingsAdapter, createManifestHydrationScope, createResourceRecordCodec, createRuntimeActionDataAccess, createUrlCache, defineFieldWithImplementation, dehydrateBuroData, deserializeResourceState, disposeBuroRequest, en, fieldImplementation, filterFields, formatBytes, generateRouteDescriptors, getResourceParams, hostPageFor, hostPageRef, hydrateBuroData, hydrateManifest, implementBlock, implementFrame, isCustomPageRoute, matchesAccept, mergeComponents, modeForSurface, normalizeServerRecordIssues, pageAccessFor, policyRecordOf, policyRowsOf, quickAnchorRef, resolveCountExecution, resolveFieldImplementation, resolveRoute, resolveValidationMessage, resourceFlowsOn, retryRecordRead, ru, serializeBuroHydration, serializeResourceState, sharedUrlCache, useApp, useAppActions, useApplicationSnapshot, useAudit, useAvailability, useAvailabilityGate, useAvailabilityResolver, useCandidatesAccess, useCapability, useCapabilityResolver, useChangeLocale, useCheckGate, useChecks, useCommandPalette, useComponent, useCount, useCreateOne, useCtx, useCurrentResource, useCurrentUIActivation, useCurrentUserId, useCustomMutation, useCustomQuery, useDataRuntime, useDebouncedValue, useDefaultComponent, useDeleteMany, useDeleteOne, useDismissExportRun, useDismissGuard, useDismissPending, useDispatcher, useDispatcherEffects, useDoc, useDocPanel, useDocScopes, useDocTopic, useDocTopics, useDocTrailScopes, useDocsConfig, useDocumentedScopes, useEntryGate, useEnumValues, useExportRuns, useFieldAccess, useFieldImplementation, useFieldImplementationCatalog, useFieldMeta, useFieldMetaSources, useFileField, useFileStorage, useFileUrls, useFilterFields, useFlowFormCatalog, useFlowOpenImplementation, useFlowRowBatch, useFlowRowResolver, useFormController, useFrameImplementations, useFramePiece, useFrameT, useHasDocTopics, useHostPage, useI18nCaps, useIdentity, useInfiniteList, useIsAuthenticated, useItemActions, useLanguageStorageKey, useLayerRuntime, useLayerStatus, useList, useListAccess, useLocalStorage, useLocale, useLogin, useLogout, useMany, useNavigationBlocker, useNavigationGuardText, useNavigationHold, useOne, useOptionalAuthController, useOptionalDataRuntime, useOptionalFileStorage, useOptionalLocale, useOptionalRecordFrame, useOptionalRouterAdapter, useOptionalT, useOptionalUIActivationSurface, useOptionalUIInstance, useOptionalUIPresentation, useOptionalVisibilityCtx, usePageAccess, usePageFlowGate, usePageScreen, usePermissions, usePlacement, usePresentBlock, usePresentFlow, useProjectedRecord, useProjectedTitle, useProjection, usePublishDocVariant, useQuick, useQuickEntry, useQuickEntryValue, useQuickOpener, useRecordFrame, useRecordTitle, useRefresh, useRegisterNavigationGuardDialog, useRegisterNotificationSink, useRelatedRecord, useResource, useResourceExport, useResourceI18n, useResourceList, useResourceListContext, useResourceUrlState, useRouteListImplementation, useRouteNavigate, useRouterAdapter, useSavedViews, useScopeCtx, useScreenState, useSearchParamsWriter, useSetSlice, useT, useTaskRun, useTestIds, useTrailHasDocs, useTranslate, useUIInstance, useUIPresent, useUIPresentationRuntime, useUiSettings, useUpdateMany, useUpdateOne, useViewRow, useVisibilityCtx, validateBuroHydration };
5290
6826
  //# sourceMappingURL=index.d.ts.map