Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions Documentation/components-package/binding-registry.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,20 @@ without unwinding the previous run. `clearBindings()` exists because the registr
right for a host that registers once at startup, wrong for Studio switching between projects or a spec that
must not inherit what the previous one registered.

## Opt-in exact lookup identities

`queryInputForm` can use `registerQueryIdentity(name, sourceIdentity, proxy)` to distinguish source-name
collisions from hot-reload replacement. `resolveExactQuery(name)` returns a constructor, `'ambiguous'`, or
`undefined`; `unregisterQueryIdentity(name, sourceIdentity)` removes a source. `resolveQuery` keeps
legacy last-registration-wins precedence; only when no legacy registration exists does it resolve a
unique identity. Multiple identity-only candidates return `undefined`, producing the existing adapters'
visible unresolved placeholder without selection. Existing adapters look up on host render, not by
subscription. `registeredQueryNames()` includes the sorted union of both namespaces, deduplicated and
including ambiguous names. Registering a lookup in both namespaces makes strict `resolveExactQuery`
ambiguous, while legacy resolution still prefers its legacy registration. Stage can emit one identity
registration per source for the same semantic name and serve both old adapters and `queryInputForm`. See the [query input form contract](query-input-form.md#collision-aware-registration-for-stage)
for the Stage emission pattern, lifecycle and migration boundary.

## The classes are not typed as Arc types

`registerQuery` takes a `BoundConstructor` — "something that can be constructed" — not
Expand Down
5 changes: 5 additions & 0 deletions Documentation/components-package/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ and re-renders when the read model changes.
| Name | Wraps | Properties | Slots |
|---|---|---|---|
| `commandForm` | `AutoCommandForm` | **`command`** (binding), `exclude` | — |
| `queryInputForm` | Native web inputs + existing optional-single runtime | **`query`** (exact binding), `inputs`, `resultField`, `label`, `submitLabel` | — |

[`queryInputForm`](query-input-form.md) is opt-in and submits explicit string drafts, never querying while
editing. Unlike forgiving display properties, malformed input declarations fail visibly and execute
nothing. It does not use command fields or a new core form schema.

`AutoCommandForm` generates its fields from the command's own property descriptors, so the form follows the
command rather than going stale when a property is added on the backend. `exclude` keeps it from generating a
Expand Down
5 changes: 3 additions & 2 deletions Documentation/components-package/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ package delivers.

## What is in the box

Thirty-seven abstract names across seven families:
Abstract names across seven families:

| Family | Names | What they wrap |
|---|---|---|
| Pages | `page`, `dataPage`, `formElement` | `Page`, `DataPage`, `FormElement` |
| Data | `dataTable`, `table`, `observableDataTable` | `DataTableForQuery`, `DataTableForObservableQuery` |
| Forms | `commandForm` and twelve field types | `AutoCommandForm` and the `CommandForm` fields |
| Forms | `commandForm`, `queryInputForm` and twelve field types | `AutoCommandForm`, command fields, and an opt-in string-input query form |
| Dialogs | `dialog`, `confirmationDialog`, `busyIndicatorDialog`, `commandDialog`, `stepperCommandDialog` | `Dialogs` and `CommandDialog` |
| Common | `icon`, `tooltip`, `dropdown`, `errorBoundary` | `IconDisplay`, `Tooltip`, `Dropdown`, `ErrorBoundary` |
| Editors | `objectContentEditor`, `objectNavigationalBar`, `schemaEditor`, `timeMachine`, `filterPanel` | The editing and inspection surfaces |
Expand Down Expand Up @@ -88,6 +88,7 @@ before you build anything on this package; everything else here assumes it.
## Where to go next

- [The binding registry](binding-registry.md) — how a screen names a query, and a host supplies it.
- [Query input form](query-input-form.md) — editable string arguments committed on submit, with exact optional-single binding.
- [Naming and shadowing](naming-and-shadowing.md) — why `table` resolves here and not to PrimeReact.
- [Theming through design tokens](theming.md) — how a Scene theme drives the library.
- [What this package does not cover](coverage.md) — and why each omission is deliberate.
Expand Down
129 changes: 129 additions & 0 deletions Documentation/components-package/query-input-form.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
---
title: Query input form
description: An opt-in web form for explicit string arguments to an optional-single Arc query.
---

`Cratis.Components:queryInputForm` owns local string drafts and commits them only on form submission
(the submit button or native Enter submission). It uses the same private `SingleResultRuntime` as
`singleResult`, not another executor. No default screen, platform-neutral Form schema, expression
language, command context or cross-component binding is introduced.

## Host and Scene payload

The host supplies the actual generated Arc proxy and its Arc context. Stage can emit the following
registration and ExternalComponent properties; these are package contracts, **not new Screenplay syntax**:

```typescript
import { registerQueryIdentity, type QueryInput } from '@cratis/scene.components';
import { ProjectName } from './Projects/Queries/ProjectName';

// Identity is a stable, unique generated source identity, not the entered project identifier.
registerQueryIdentity('ProjectName', 'Projects/Queries/ProjectName', ProjectName);

const inputs: QueryInput[] = [
{ parameter: 'projectId', type: 'string', label: 'Project identifier' },
];
const properties = {
query: 'ProjectName',
inputs,
resultField: 'name',
label: 'Find project',
submitLabel: 'Search',
};
// Place properties on an ExternalComponent with componentName 'Cratis.Components:queryInputForm'.
```

Use the **exact case-sensitive query name and argument names** emitted by the proxy. There is no route
in the form, no guessed project, no all-projects fallback and no parameter inference. It requires a
zero-argument native optional-single proxy (`enumerable === false`). Enumerable proxies fail before
`perform`. The host supplies Arc/Arc React 22.16.1 or compatible peers as for `singleResult`, plus the
matching Fundamentals peer (7.19.2 or compatible, already required by Arc) for `Guid` constructor identity.

| Property | Contract |
|---|---|
| `query` | Required exact registered name. Missing/ambiguous bindings are visible and do not execute. |
| `inputs` | Required nonempty array of string input declarations. Duplicate parameter names or any malformed entry reject the entire form visibly. |
| `resultField` | Required nonempty name of one own scalar result field; not a property path. |
| `label` | Accessible form name; defaults to `Query input`. |
| `submitLabel` | Submit button text; defaults to `Search`. |

Each input has a nonempty string `parameter`, literal `type: 'string'`, nonempty string `label`, optional
boolean `required` (defaults to true), and optional string `pattern`. `pattern` is an authored whole-value
JavaScript Unicode regular expression without delimiters, for example `[A-Z]{2}-[0-9]{2}`. Invalid regular
expressions are configuration errors. There is no inferred GUID/email/business format. Required inputs
reject empty or whitespace-only strings. Other values, including surrounding whitespace, remain exact:
**no trimming, coercion or generated identifiers**. Optional empty strings remain present in the snapshot;
proxy required-argument rules may still reject them. No initial/default argument values or slots are read.
The pattern wrapper uses Unicode mode without multiline mode: final newline characters do not satisfy
an otherwise digit-only pattern. Values explicitly allowed by a pattern are still submitted unchanged.

At the actual lazy native boundary, every declared argument must match **exactly one** proxy
`parameterDescriptors` entry by exact name. Only scalar `String` and `Guid` constructor types are
supported for `type: 'string'`; numeric, boolean, unknown/missing, duplicate or enumerable descriptors
fail visibly before `perform`/HTTP. This is a bounded form mapping check, not type inference or a change
to `singleResult`'s host-supplied arguments. Strings are never converted into Guid objects. A generated
`projectId: Guid` with `new ParameterDescriptor('projectId', Guid, false)` is supported unchanged.
Arc 22.16.1 does **not** enforce descriptor argument types or GUID string format in native `perform`.
If format enforcement is desired, Stage must supply an explicit input `pattern` (or the proxy/backend
must validate it); a nonempty malformed GUID can otherwise reach HTTP unchanged.

## Submission and lifetime

- Controls are accessible, controlled native web text inputs with associated labels, required state,
inline validation alerts, `aria-invalid` and `aria-describedby`. Components' `InputTextField` is wrapped
in `asCommandFormField` and requires command context; it is deliberately not repurposed with a fake command.
- Editing never imports the execution runtime or calls a query. Submission validates required values and
declared formats first. Invalid inputs stay visible and make no native `perform` or HTTP call.
- Valid submission creates a **new frozen argument object** containing only the declared exact names and
strings. Repeated submission of identical values explicitly retries, including a pending request.
- Editing any draft immediately hides a settled result and unmounts/cancels a pending runtime. Editing
back to the previous string does not resurrect that result: another submission is required.
- Native `QueryFor.perform` still owns proxy validation, required arguments, routing, HTTP and
deserialization. A generated-validator rejection is visible as `Unable to load result`, with no HTTP;
drafts remain editable. Scene does not expose server exception details or duplicate proxy/business
validators. The form-only descriptor compatibility check above precedes native validation.
- Result, `Not found`, loading/failure, Arc context changes, abort and stale-response suppression are the
existing `singleResult` behavior. Result strings are escaped text; only own string/finite-number/boolean
fields are displayed. Successful missing models are not collections.
- Equivalent host rerenders retain drafts. A changed query name/input declaration/result field starts a
fresh session. Disabling the Scene element, losing the binding or unmounting cancels and discards it.
A replacement constructor for the same source identity reexecutes committed arguments through the
existing runtime (and cancels the old generation). Arc context changes do the same.

## Collision-aware registration for Stage

`registerQuery`/`registerQueries` **still replace by name** for hot reload. `resolveQuery` always uses
that legacy registration when present, even if there are identity registrations. Without a legacy
registration it returns the unique identity-only constructor. Zero or multiple identities return
`undefined`; existing tables/data pages/`singleResult` show their unresolved-binding placeholder and
select no candidate. These existing adapters retain their render-time lookup contract (host rerender
is needed after registry changes); command resolution is unchanged.

For collision detection, Stage should emit **one `registerQueryIdentity(name, sourceIdentity, proxy)`
call per source**, not first collapse exports into a name-keyed object. The same `(name, sourceIdentity)`
replaces on hot reload; two different identities are ambiguous even if they share a constructor.
`unregisterQueryIdentity(name, sourceIdentity)` removes a deleted/renamed source. `clearBindings()` clears
both namespaces, including when switching applications.

`resolveExactQuery(name)` remains stricter: it returns a constructor, `'ambiguous'`, or `undefined`,
counting a legacy registration as one additional candidate. Thus mixed legacy/identity registrations
are ambiguous for `queryInputForm`, even though legacy adapters still prefer the legacy registration.
Stage should emit **only the identity registration per source for the same semantic name**; there is
no need to duplicate `registerQueries` for existing authored tables or `singleResult`.
`registeredQueryNames()` is the sorted, deduplicated union of both namespaces, including ambiguous
names. Removing the final identity removes that name unless a legacy registration remains.
`subscribeQueryBindings(listener)` notifies live exact-binding consumers; `queryInputForm` observes it
and cancels immediately if a collision appears.

## Verification boundary

The regression suite renders the real `SceneElementView` with native Arc 22.16.1 `QueryFor.perform`,
replacing only HTTP. It covers drafts, snapshots, validation, retry, exact/missing/ambiguous bindings,
result/not-found, cancellation and stale completion, including the canonical generated ProjectById
shape with Guid descriptors, decorated ProjectSummary, empty-object default and native inherited
`perform`. Native single-result tests and table adapter forwarding tests cover the identity bridge.
These are consumer-shape tests, not execution of a generated application. The DOM environment dispatches the form submit
event; jsdom does not simulate a browser's implicit Enter submission algorithm.

This is a bounded Scene #39 increment, not standalone generated-application/browser acceptance, a Stage
composition change, a general result-binding model or issue closure.
43 changes: 39 additions & 4 deletions Source/JavaScript/components/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,44 @@ view rather than an invented retry protocol. Enumerable proxies are rejected bef

Tests use a real `QueryFor` subclass with unmodified `perform`, native validation and only the HTTP
boundary replaced. Separate lazy-boundary doubles test Arc-free gating, not native client correctness.
They do not prove a generated server route, backend persistence or end-to-end editable input. Scene #39
must remain open: editable Scene parameter binding, input/commit semantics, cross-component binding and
a general result-binding model are still unimplemented. No default profile, generator or schema changed.
They do not prove a generated server route or backend persistence. The opt-in `queryInputForm` below
adds form-local editable input/commit semantics; Scene #39 remains open for broader binding and acceptance.
No default profile, generator or schema changed.

## Query input form (opt-in)

`Cratis.Components:queryInputForm` composes controlled native web string inputs with the **existing**
`SingleResultRuntime`. Its package-local `QueryInput` declarations name exact parameters, labels,
required state and optional whole-string patterns. It validates on submit, freezes a fresh committed
snapshot, cancels/hides obsolete results on edit and supports repeat submit. There is no query while
editing, fake command context, new executor, core schema, default identifier or inferred route.

Published Components `InputTextField` is wrapped with `asCommandFormField` and consumes command context;
it is not an exported independent controlled input. Native labelled text inputs are deliberately used
instead. The existing command-field adapters and command-slot behavior are unchanged.

`registerQueryIdentity(name, sourceIdentity, proxy)` is the explicit opt-in collision-aware API:
identical identities replace on hot reload; different sources with the same name are ambiguous.
`queryInputForm` subscribes to exact-binding changes. Legacy `registerQuery` keeps its last-write-wins
semantics and is authoritative for legacy `resolveQuery` when present. Otherwise a unique identity-only
registration also serves existing tables and `singleResult`; multiple identities show their unresolved
placeholder without selecting a candidate. Existing adapters re-resolve on host render.
`registeredQueryNames` includes the sorted union of both namespaces. Strict `resolveExactQuery` still
counts mixed candidates as ambiguous: Stage should emit one identity registration per source, not also
register the same name through `registerQueries`.

At the existing lazy runtime boundary, form declarations must map exactly to scalar `String` or `Guid`
proxy descriptors. Unsupported/missing/duplicate descriptors fail before HTTP; no coercion is performed.
The canonical generated Guid proxy shape is tested unchanged. Native Arc validates required arguments
and proxy rules, but does not automatically validate GUID format: declare an explicit pattern if needed.
The lazy runtime uses the optional Fundamentals peer already required by Arc; no Components peer-floor
change is required.

The [public contract](../../../Documentation/components-package/query-input-form.md) documents the exact
payload and Stage consumption pattern. Tests render through real `SceneElementView`, unmodified native
Arc `QueryFor.perform` and only substituted HTTP, including validation, stale requests and unmount.
They are not generated/browser acceptance; the DOM suite dispatches submit rather than simulating a
browser's implicit Enter algorithm.

## Naming and shadowing

Expand Down Expand Up @@ -199,7 +234,7 @@ both packages write themes in one language: `primary.color`, `primary.contrastCo
bindings/ the registry, BindingKind, BoundConstructor, MissingBinding, ArcRuntimeBoundary
pages/ page, dataPage, formElement
data/ dataTable, table, observableDataTable, singleResult
forms/ commandForm; forms/fields/ the twelve field types
forms/ commandForm, queryInputForm; forms/fields/ the twelve field types
dialogs/ dialog, confirmationDialog, busyIndicatorDialog, commandDialog, stepperCommandDialog
common/ icon, tooltip, dropdown, errorBoundary
editors/ objectContentEditor, objectNavigationalBar, schemaEditor, timeMachine, filterPanel
Expand Down
Loading
Loading