@fougere/app 0.16.3-alpha.0 → 0.16.5-alpha.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/Choices.d.ts +20 -0
- package/dist/Choices.d.ts.map +1 -0
- package/dist/Choices.js +20 -0
- package/dist/Choices.js.map +1 -0
- package/dist/FormEntity.d.ts +1 -8
- package/dist/FormEntity.d.ts.map +1 -1
- package/dist/FormEntity.js +19 -1
- package/dist/FormEntity.js.map +1 -1
- package/dist/FormField.d.ts +12 -1
- package/dist/FormField.d.ts.map +1 -1
- package/dist/FormRow.d.ts +9 -0
- package/dist/FormRow.d.ts.map +1 -0
- package/dist/FormRow.js +2 -0
- package/dist/FormRow.js.map +1 -0
- package/dist/RowName.d.ts +7 -0
- package/dist/RowName.d.ts.map +1 -0
- package/dist/RowName.js +10 -0
- package/dist/RowName.js.map +1 -0
- package/dist/client/index.d.ts +4 -2
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -0
- package/dist/client/index.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +12 -12
- package/src/Choices.ts +40 -0
- package/src/FormEntity.ts +21 -14
- package/src/FormField.ts +13 -1
- package/src/FormRow.ts +11 -0
- package/src/RowName.ts +12 -0
- package/src/client/index.ts +4 -2
- package/src/index.ts +4 -2
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Fetcher } from './Fetcher.js';
|
|
2
|
+
import type { FormReference } from './FormField.js';
|
|
3
|
+
/** One row a reference may point at, as a form offers it. */
|
|
4
|
+
export interface Choice {
|
|
5
|
+
value: string;
|
|
6
|
+
label: string;
|
|
7
|
+
}
|
|
8
|
+
/** What one load offers, and whether the target holds more than it — which is when a search is worth showing. */
|
|
9
|
+
export interface Offer {
|
|
10
|
+
choices: Choice[];
|
|
11
|
+
more: boolean;
|
|
12
|
+
}
|
|
13
|
+
/** The rows a reference field offers — a handful, and a search to reach the others. */
|
|
14
|
+
export declare class Choices {
|
|
15
|
+
/** Enough to choose among at a glance; the rest is what a search is for. */
|
|
16
|
+
static readonly LIMIT = 20;
|
|
17
|
+
/** The first rows of the target, or those whose label contains `text`. */
|
|
18
|
+
static of(fetcher: Fetcher, reference: FormReference, text?: string): Promise<Offer>;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=Choices.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Choices.d.ts","sourceRoot":"","sources":["../src/Choices.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEpD,6DAA6D;AAC7D,MAAM,WAAW,MAAM;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,iHAAiH;AACjH,MAAM,WAAW,KAAK;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,EAAE,OAAO,CAAC;CACf;AAED,uFAAuF;AACvF,qBAAa,OAAO;IAClB,4EAA4E;IAC5E,MAAM,CAAC,QAAQ,CAAC,KAAK,MAAM;IAE3B,0EAA0E;IAC1E,OAAa,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,IAAI,SAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAgBrF;CACF"}
|
package/dist/Choices.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { invocationOf, itemsOf, sendCall } from './Designation.js';
|
|
2
|
+
/** The rows a reference field offers — a handful, and a search to reach the others. */
|
|
3
|
+
export class Choices {
|
|
4
|
+
/** Enough to choose among at a glance; the rest is what a search is for. */
|
|
5
|
+
static LIMIT = 20;
|
|
6
|
+
/** The first rows of the target, or those whose label contains `text`. */
|
|
7
|
+
static async of(fetcher, reference, text = '') {
|
|
8
|
+
const where = text ? { [reference.label]: { contains: text } } : undefined;
|
|
9
|
+
const answer = await sendCall(fetcher, { entity: reference.to, op: 'list' }, invocationOf({ query: { limit: Choices.LIMIT + 1, ...(where ? { where } : {}) } }));
|
|
10
|
+
const rows = itemsOf(answer);
|
|
11
|
+
return {
|
|
12
|
+
choices: rows.slice(0, Choices.LIMIT).map((row) => ({
|
|
13
|
+
value: String(row[reference.key]),
|
|
14
|
+
label: String(row[reference.label] ?? row[reference.key]),
|
|
15
|
+
})),
|
|
16
|
+
more: rows.length > Choices.LIMIT,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=Choices.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Choices.js","sourceRoot":"","sources":["../src/Choices.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAgBnE,uFAAuF;AACvF,MAAM,OAAO,OAAO;IAClB,4EAA4E;IAC5E,MAAM,CAAU,KAAK,GAAG,EAAE,CAAC;IAE3B,0EAA0E;IAC1E,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,OAAgB,EAAE,SAAwB,EAAE,IAAI,GAAG,EAAE;QACnE,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QAC3E,MAAM,MAAM,GAAG,MAAM,QAAQ,CAC3B,OAAO,EACP,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,EACpC,YAAY,CAAC,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,GAAG,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CACnF,CAAC;QACF,MAAM,IAAI,GAAG,OAAO,CAA0B,MAAM,CAAC,CAAC;QAEtD,OAAO;YACL,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;gBAClD,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;gBACjC,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;aAC1D,CAAC,CAAC;YACH,IAAI,EAAE,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,KAAK;SAClC,CAAC;IACJ,CAAC;CACF"}
|
package/dist/FormEntity.d.ts
CHANGED
|
@@ -1,16 +1,9 @@
|
|
|
1
1
|
import type { SchemaView, ValidationError } from '@fougere/schema';
|
|
2
2
|
import type { FormField } from './FormField.js';
|
|
3
|
+
import type { FormErrors, FormFieldName } from './FormRow.js';
|
|
3
4
|
import type { TableColumn } from './TableColumn.js';
|
|
4
5
|
/** What an entity class exposes to a form — the schema statics it already has. */
|
|
5
6
|
export type FormEntity = SchemaView;
|
|
6
|
-
/** The row an entity class builds, which is what its form fills and what `create` answers. */
|
|
7
|
-
export type FormRow<E> = E extends abstract new (...args: never[]) => infer Row ? Row : Record<string, unknown>;
|
|
8
|
-
/** What the form holds — any field, none of them yet. */
|
|
9
|
-
export type FormValues<E> = Partial<FormRow<E>>;
|
|
10
|
-
/** A field of that row, by name. */
|
|
11
|
-
export type FormFieldName<E> = keyof FormRow<E> & string;
|
|
12
|
-
/** One message per refused field. */
|
|
13
|
-
export type FormErrors<E> = Partial<Record<FormFieldName<E>, string>>;
|
|
14
7
|
/** The fields a create form is made of. */
|
|
15
8
|
export declare function formFieldsOf<E extends FormEntity>(entity: E, entityKey: string): FormField<FormFieldName<E>>[];
|
|
16
9
|
/** The columns a list is made of. */
|
package/dist/FormEntity.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormEntity.d.ts","sourceRoot":"","sources":["../src/FormEntity.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAS,UAAU,EAAa,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACrF,OAAO,KAAK,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"FormEntity.d.ts","sourceRoot":"","sources":["../src/FormEntity.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAS,UAAU,EAAa,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACrF,OAAO,KAAK,EAAE,SAAS,EAAiB,MAAM,gBAAgB,CAAC;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,kFAAkF;AAClF,MAAM,MAAM,UAAU,GAAG,UAAU,CAAC;AAwFpC,2CAA2C;AAC3C,wBAAgB,YAAY,CAAC,CAAC,SAAS,UAAU,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,EAAE,CAsB9G;AAqBD,qCAAqC;AACrC,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,GAAG,WAAW,EAAE,CAanF;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CACvB,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQzB;AAED,+FAA+F;AAC/F,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,eAAe,EAAE,GAAG,UAAU,CAAC,CAAC,CAAC,CASzE"}
|
package/dist/FormEntity.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Lifecycle } from '@fougere/schema';
|
|
2
|
-
import { Shapes, lowerFirst, Role, Visibility } from '@fougere/schema';
|
|
2
|
+
import { FieldSet, Shapes, lowerFirst, Role, Visibility } from '@fougere/schema';
|
|
3
|
+
import { rowNameOf } from './RowName.js';
|
|
3
4
|
/** The literal a field is born with, when it declares one. */
|
|
4
5
|
function defaultOf(field) {
|
|
5
6
|
return Lifecycle.of(field).literal?.value;
|
|
@@ -21,6 +22,8 @@ const CONTROL_BY_TYPE = {
|
|
|
21
22
|
array: 'text',
|
|
22
23
|
};
|
|
23
24
|
function controlOf(field) {
|
|
25
|
+
if (Role.of(field).isReference())
|
|
26
|
+
return 'reference';
|
|
24
27
|
if (enumOf(field))
|
|
25
28
|
return 'select';
|
|
26
29
|
const type = Shapes.typeOf(field.shape);
|
|
@@ -30,6 +33,19 @@ function controlOf(field) {
|
|
|
30
33
|
const format = base?.type === 'string' ? base.format : undefined;
|
|
31
34
|
return (format && CONTROL_BY_FORMAT[format]) ?? 'text';
|
|
32
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* What a reference field chooses among. The label is the first text the target shows — never a
|
|
38
|
+
* `writeOnly` one, since a choice is read by whoever fills the form — and its key when it shows none,
|
|
39
|
+
* or when the target is known only by name.
|
|
40
|
+
*/
|
|
41
|
+
function referenceOf(field) {
|
|
42
|
+
const target = Role.of(field).target;
|
|
43
|
+
if (!target)
|
|
44
|
+
return undefined;
|
|
45
|
+
const fields = target.getFields?.() ?? {};
|
|
46
|
+
const key = FieldSet.of(fields).primary ?? 'id';
|
|
47
|
+
return { to: lowerFirst(target.name), key, label: rowNameOf(fields) ?? key };
|
|
48
|
+
}
|
|
33
49
|
/** A closed set's members, when the shape declares one — `oneOf('draft','live')`. */
|
|
34
50
|
function enumOf(field) {
|
|
35
51
|
const base = Shapes.of(field.shape).base;
|
|
@@ -68,12 +84,14 @@ export function formFieldsOf(entity, entityKey) {
|
|
|
68
84
|
const required = Lifecycle.of(f).requiredAtCreate();
|
|
69
85
|
const attrs = attrsOf(f, control, required);
|
|
70
86
|
const members = enumOf(f);
|
|
87
|
+
const reference = referenceOf(f);
|
|
71
88
|
return {
|
|
72
89
|
name,
|
|
73
90
|
control,
|
|
74
91
|
required,
|
|
75
92
|
...labelOf(name, entityKey),
|
|
76
93
|
...(members ? { options: members.filter((value) => value !== null) } : {}),
|
|
94
|
+
...(reference ? { reference } : {}),
|
|
77
95
|
...(Object.keys(attrs).length ? { attrs } : {}),
|
|
78
96
|
...(defaultOf(f) !== undefined ? { default: defaultOf(f) } : {}),
|
|
79
97
|
};
|
package/dist/FormEntity.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormEntity.js","sourceRoot":"","sources":["../src/FormEntity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"FormEntity.js","sourceRoot":"","sources":["../src/FormEntity.ts"],"names":[],"mappings":"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"}
|
package/dist/FormField.d.ts
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
|
+
/** The entity a reference points at, and which of its fields names one of its rows. */
|
|
2
|
+
export interface FormReference {
|
|
3
|
+
/** Its address — what a call to it names. */
|
|
4
|
+
to: string;
|
|
5
|
+
/** Its primary field: what the reference holds. */
|
|
6
|
+
key: string;
|
|
7
|
+
/** What a choice shows, and what a search looks in. */
|
|
8
|
+
label: string;
|
|
9
|
+
}
|
|
1
10
|
export interface FormField<Name extends string = string> {
|
|
2
11
|
name: Name;
|
|
3
12
|
/** Rendering hint derived from the shape — the page maps it to widgets. */
|
|
4
|
-
control: 'text' | 'email' | 'url' | 'number' | 'boolean' | 'date' | 'select';
|
|
13
|
+
control: 'text' | 'email' | 'url' | 'number' | 'boolean' | 'date' | 'select' | 'reference';
|
|
5
14
|
required: boolean;
|
|
6
15
|
/** i18n key by convention: `entity.field`. The schema never carries display text. */
|
|
7
16
|
labelKey: string;
|
|
@@ -9,6 +18,8 @@ export interface FormField<Name extends string = string> {
|
|
|
9
18
|
label: string;
|
|
10
19
|
/** Enum values, when control is 'select'. */
|
|
11
20
|
options?: (string | number)[];
|
|
21
|
+
/** What a `reference` control chooses among. */
|
|
22
|
+
reference?: FormReference;
|
|
12
23
|
/**
|
|
13
24
|
* What the browser enforces, under the names it already knows — spread this on the input and the
|
|
14
25
|
* page states no rule of its own.
|
package/dist/FormField.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormField.d.ts","sourceRoot":"","sources":["../src/FormField.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM;IACrD,IAAI,EAAE,IAAI,CAAC;IACX,2EAA2E;IAC3E,OAAO,EAAE,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"FormField.d.ts","sourceRoot":"","sources":["../src/FormField.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,MAAM,WAAW,aAAa;IAC5B,6CAA6C;IAC7C,EAAE,EAAE,MAAM,CAAC;IACX,mDAAmD;IACnD,GAAG,EAAE,MAAM,CAAC;IACZ,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,SAAS,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM;IACrD,IAAI,EAAE,IAAI,CAAC;IACX,2EAA2E;IAC3E,OAAO,EAAE,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,WAAW,CAAC;IAC3F,QAAQ,EAAE,OAAO,CAAC;IAClB,qFAAqF;IACrF,QAAQ,EAAE,MAAM,CAAC;IACjB,yDAAyD;IACzD,KAAK,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,OAAO,CAAC,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IAC9B,gDAAgD;IAChD,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B;;;OAGG;IACH,KAAK,CAAC,EAAE;QACN,IAAI,CAAC,EAAE,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC;QAClC,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,wFAAwF;IACxF,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** The row an entity class builds, which is what its form fills and what `create` answers. */
|
|
2
|
+
export type FormRow<E> = E extends abstract new (...args: never[]) => infer Row ? Row : Record<string, unknown>;
|
|
3
|
+
/** What the form holds — any field, none of them yet. */
|
|
4
|
+
export type FormValues<E> = Partial<FormRow<E>>;
|
|
5
|
+
/** A field of that row, by name. */
|
|
6
|
+
export type FormFieldName<E> = keyof FormRow<E> & string;
|
|
7
|
+
/** One message per refused field. */
|
|
8
|
+
export type FormErrors<E> = Partial<Record<FormFieldName<E>, string>>;
|
|
9
|
+
//# sourceMappingURL=FormRow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormRow.d.ts","sourceRoot":"","sources":["../src/FormRow.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,MAAM,MAAM,OAAO,CAAC,CAAC,IAAI,CAAC,SAAS,QAAQ,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,MAAM,GAAG,GAAG,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEhH,yDAAyD;AACzD,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;AAEhD,oCAAoC;AACpC,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC;AAEzD,qCAAqC;AACrC,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC"}
|
package/dist/FormRow.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormRow.js","sourceRoot":"","sources":["../src/FormRow.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type Fields } from '@fougere/schema';
|
|
2
|
+
/**
|
|
3
|
+
* The field that names a row: the first text a reader may see that is neither its key nor a
|
|
4
|
+
* reference. A choice shows it, a line of a list leads with it, and a search looks in it.
|
|
5
|
+
*/
|
|
6
|
+
export declare function rowNameOf(fields: Fields): string | undefined;
|
|
7
|
+
//# sourceMappingURL=RowName.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RowName.d.ts","sourceRoot":"","sources":["../src/RowName.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAExE;;;GAGG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAK5D"}
|
package/dist/RowName.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Role, Shapes, Visibility } from '@fougere/schema';
|
|
2
|
+
/**
|
|
3
|
+
* The field that names a row: the first text a reader may see that is neither its key nor a
|
|
4
|
+
* reference. A choice shows it, a line of a list leads with it, and a search looks in it.
|
|
5
|
+
*/
|
|
6
|
+
export function rowNameOf(fields) {
|
|
7
|
+
const named = Object.entries(Visibility.of(fields).output).find(([, field]) => Shapes.typeOf(field.shape) === 'text' && !Role.of(field).isPrimary() && !Role.of(field).isRelation());
|
|
8
|
+
return named?.[0];
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=RowName.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RowName.js","sourceRoot":"","sources":["../src/RowName.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAe,MAAM,iBAAiB,CAAC;AAExE;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,MAAc;IACtC,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAC5E,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,MAAM,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC;IAExG,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC"}
|
package/dist/client/index.d.ts
CHANGED
|
@@ -3,8 +3,10 @@ export { type CallInput } from '../CallInput.js';
|
|
|
3
3
|
export { type Fetcher } from '../Fetcher.js';
|
|
4
4
|
export { type Designation, CALL_ENDPOINT, addressOf, asFougereError, callOf, entityKeyOf, facade, facadeOf, fetcher, invocationOf, itemsOf, mountedKeys, onRefetch, pageOf, queryKeyOf, revalidate, sendCall, trackQuery, } from '../Designation.js';
|
|
5
5
|
export type { Addresses, Answer, AnyHandler, FacadeName, FougereHandlers, FougereOperations, HandlerOf, Refused, Rows, } from '@fougere/core/contract';
|
|
6
|
-
export { errorsByField, formFieldsOf, payloadOf, tableColumnsOf, type FormEntity
|
|
7
|
-
export { type
|
|
6
|
+
export { errorsByField, formFieldsOf, payloadOf, tableColumnsOf, type FormEntity } from '../FormEntity.js';
|
|
7
|
+
export { type FormRow, type FormValues, type FormErrors, type FormFieldName } from '../FormRow.js';
|
|
8
|
+
export { type FormField, type FormReference } from '../FormField.js';
|
|
9
|
+
export { Choices, type Choice, type Offer } from '../Choices.js';
|
|
8
10
|
export { type TableColumn } from '../TableColumn.js';
|
|
9
11
|
export { sessionViewOf, type SessionView } from '../session.js';
|
|
10
12
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EACL,KAAK,WAAW,EAChB,aAAa,EACb,SAAS,EACT,cAAc,EACd,MAAM,EACN,WAAW,EACX,MAAM,EACN,QAAQ,EACR,OAAO,EACP,YAAY,EACZ,OAAO,EACP,WAAW,EACX,SAAS,EACT,MAAM,EACN,UAAU,EACV,UAAU,EACV,QAAQ,EACR,UAAU,GACX,MAAM,mBAAmB,CAAC;AAE3B,YAAY,EACV,SAAS,EACT,MAAM,EACN,UAAU,EACV,UAAU,EACV,eAAe,EACf,iBAAiB,EACjB,SAAS,EACT,OAAO,EACP,IAAI,GACL,MAAM,wBAAwB,CAAC;AAKhC,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,KAAK,UAAU,EAAE,KAAK,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EACL,KAAK,WAAW,EAChB,aAAa,EACb,SAAS,EACT,cAAc,EACd,MAAM,EACN,WAAW,EACX,MAAM,EACN,QAAQ,EACR,OAAO,EACP,YAAY,EACZ,OAAO,EACP,WAAW,EACX,SAAS,EACT,MAAM,EACN,UAAU,EACV,UAAU,EACV,QAAQ,EACR,UAAU,GACX,MAAM,mBAAmB,CAAC;AAE3B,YAAY,EACV,SAAS,EACT,MAAM,EACN,UAAU,EACV,UAAU,EACV,eAAe,EACf,iBAAiB,EACjB,SAAS,EACT,OAAO,EACP,IAAI,GACL,MAAM,wBAAwB,CAAC;AAKhC,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC3G,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AACnG,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrE,OAAO,EAAE,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,KAAK,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/client/index.js
CHANGED
|
@@ -3,5 +3,6 @@ export { CALL_ENDPOINT, addressOf, asFougereError, callOf, entityKeyOf, facade,
|
|
|
3
3
|
// reaches the browser through this subpath rather than through the package root,
|
|
4
4
|
// which carries the boot.
|
|
5
5
|
export { errorsByField, formFieldsOf, payloadOf, tableColumnsOf } from '../FormEntity.js';
|
|
6
|
+
export { Choices } from '../Choices.js';
|
|
6
7
|
export { sessionViewOf } from '../session.js';
|
|
7
8
|
//# sourceMappingURL=index.js.map
|
package/dist/client/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,aAAa,EACb,SAAS,EACT,cAAc,EACd,MAAM,EACN,WAAW,EACX,MAAM,EACN,QAAQ,EACR,OAAO,EACP,YAAY,EACZ,OAAO,EACP,WAAW,EACX,SAAS,EACT,MAAM,EACN,UAAU,EACV,UAAU,EACV,QAAQ,EACR,UAAU,GACX,MAAM,mBAAmB,CAAC;AAc3B,+EAA+E;AAC/E,iFAAiF;AACjF,0BAA0B;AAC1B,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,cAAc,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,aAAa,EACb,SAAS,EACT,cAAc,EACd,MAAM,EACN,WAAW,EACX,MAAM,EACN,QAAQ,EACR,OAAO,EACP,YAAY,EACZ,OAAO,EACP,WAAW,EACX,SAAS,EACT,MAAM,EACN,UAAU,EACV,UAAU,EACV,QAAQ,EACR,UAAU,GACX,MAAM,mBAAmB,CAAC;AAc3B,+EAA+E;AAC/E,iFAAiF;AACjF,0BAA0B;AAC1B,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,cAAc,EAAmB,MAAM,kBAAkB,CAAC;AAG3G,OAAO,EAAE,OAAO,EAA2B,MAAM,eAAe,CAAC;AAEjE,OAAO,EAAE,aAAa,EAAoB,MAAM,eAAe,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,8 +3,10 @@ export { configureFougere, extendFougere, useFougereApp, reloadFougere, type Fou
|
|
|
3
3
|
export { authOf, useFougereAuth } from './auth.js';
|
|
4
4
|
export { tableOf } from './RouteMatch.js';
|
|
5
5
|
export { invokeOn, rpcParseError, serveRest, serveRpc, surfaceOf } from './Outcome.js';
|
|
6
|
-
export { errorsByField, formFieldsOf, payloadOf, tableColumnsOf, type FormEntity
|
|
7
|
-
export { type
|
|
6
|
+
export { errorsByField, formFieldsOf, payloadOf, tableColumnsOf, type FormEntity } from './FormEntity.js';
|
|
7
|
+
export { type FormRow, type FormValues, type FormErrors, type FormFieldName } from './FormRow.js';
|
|
8
|
+
export { type FormField, type FormReference } from './FormField.js';
|
|
9
|
+
export { Choices, type Choice, type Offer } from './Choices.js';
|
|
8
10
|
export { type TableColumn } from './TableColumn.js';
|
|
9
11
|
export { sessionViewOf, type SessionView } from './session.js';
|
|
10
12
|
export { stateFor } from './state.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,aAAa,EACb,aAAa,EACb,KAAK,mBAAmB,GACzB,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEnD,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE1C,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEvF,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,KAAK,UAAU,EAAE,KAAK,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,aAAa,EACb,aAAa,EACb,KAAK,mBAAmB,GACzB,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEnD,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE1C,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEvF,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC1G,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AAClG,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpE,OAAO,EAAE,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,KAAK,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,EAAE,aAAa,EAAE,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAE/D,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,OAAO,EAAE,YAAY,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAGjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ export { authOf, useFougereAuth } from './auth.js';
|
|
|
4
4
|
export { tableOf } from './RouteMatch.js';
|
|
5
5
|
export { invokeOn, rpcParseError, serveRest, serveRpc, surfaceOf } from './Outcome.js';
|
|
6
6
|
export { errorsByField, formFieldsOf, payloadOf, tableColumnsOf } from './FormEntity.js';
|
|
7
|
+
export { Choices } from './Choices.js';
|
|
7
8
|
export { sessionViewOf } from './session.js';
|
|
8
9
|
export { stateFor } from './state.js';
|
|
9
10
|
export { serveGraphQL } from './graphql.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,aAAa,EACb,aAAa,GAEd,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEnD,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE1C,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEvF,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,cAAc,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,aAAa,EACb,aAAa,GAEd,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEnD,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE1C,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEvF,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,cAAc,EAAmB,MAAM,iBAAiB,CAAC;AAG1G,OAAO,EAAE,OAAO,EAA2B,MAAM,cAAc,CAAC;AAGhE,OAAO,EAAE,aAAa,EAAoB,MAAM,cAAc,CAAC;AAE/D,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,OAAO,EAAE,YAAY,EAAuB,MAAM,cAAc,CAAC;AAEjE,sFAAsF;AACtF,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fougere/app",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.5-alpha.0",
|
|
4
4
|
"description": "The host-independent half of a Fougere app: boot, call envelope, REST table, form contract.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"fougere",
|
|
@@ -41,26 +41,26 @@
|
|
|
41
41
|
"src"
|
|
42
42
|
],
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@fougere/core": "0.16.
|
|
45
|
-
"@fougere/defaults": "0.16.
|
|
46
|
-
"@fougere/schema": "0.16.
|
|
47
|
-
"@fougere/adapter-rest": "0.16.
|
|
48
|
-
"@fougere/transport-http": "0.16.
|
|
44
|
+
"@fougere/core": "0.16.5-alpha.0",
|
|
45
|
+
"@fougere/defaults": "0.16.5-alpha.0",
|
|
46
|
+
"@fougere/schema": "0.16.5-alpha.0",
|
|
47
|
+
"@fougere/adapter-rest": "0.16.5-alpha.0",
|
|
48
|
+
"@fougere/transport-http": "0.16.5-alpha.0",
|
|
49
49
|
"jiti": "^2.6.1",
|
|
50
|
-
"@fougere/http": "0.16.
|
|
51
|
-
"@fougere/container": "0.16.
|
|
52
|
-
"@fougere/adapter-memory": "0.16.
|
|
53
|
-
"@fougere/compiler": "0.16.
|
|
50
|
+
"@fougere/http": "0.16.5-alpha.0",
|
|
51
|
+
"@fougere/container": "0.16.5-alpha.0",
|
|
52
|
+
"@fougere/adapter-memory": "0.16.5-alpha.0",
|
|
53
|
+
"@fougere/compiler": "0.16.5-alpha.0"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"vitest": "^4.1.0",
|
|
57
|
-
"@fougere/adapter-graphql": "0.16.
|
|
57
|
+
"@fougere/adapter-graphql": "0.16.5-alpha.0"
|
|
58
58
|
},
|
|
59
59
|
"publishConfig": {
|
|
60
60
|
"access": "public"
|
|
61
61
|
},
|
|
62
62
|
"peerDependencies": {
|
|
63
|
-
"@fougere/adapter-graphql": "^0.16.
|
|
63
|
+
"@fougere/adapter-graphql": "^0.16.5-alpha.0"
|
|
64
64
|
},
|
|
65
65
|
"peerDependenciesMeta": {
|
|
66
66
|
"@fougere/adapter-graphql": {
|
package/src/Choices.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { invocationOf, itemsOf, sendCall } from './Designation.js';
|
|
2
|
+
import type { Fetcher } from './Fetcher.js';
|
|
3
|
+
import type { FormReference } from './FormField.js';
|
|
4
|
+
|
|
5
|
+
/** One row a reference may point at, as a form offers it. */
|
|
6
|
+
export interface Choice {
|
|
7
|
+
value: string;
|
|
8
|
+
label: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** What one load offers, and whether the target holds more than it — which is when a search is worth showing. */
|
|
12
|
+
export interface Offer {
|
|
13
|
+
choices: Choice[];
|
|
14
|
+
more: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** The rows a reference field offers — a handful, and a search to reach the others. */
|
|
18
|
+
export class Choices {
|
|
19
|
+
/** Enough to choose among at a glance; the rest is what a search is for. */
|
|
20
|
+
static readonly LIMIT = 20;
|
|
21
|
+
|
|
22
|
+
/** The first rows of the target, or those whose label contains `text`. */
|
|
23
|
+
static async of(fetcher: Fetcher, reference: FormReference, text = ''): Promise<Offer> {
|
|
24
|
+
const where = text ? { [reference.label]: { contains: text } } : undefined;
|
|
25
|
+
const answer = await sendCall(
|
|
26
|
+
fetcher,
|
|
27
|
+
{ entity: reference.to, op: 'list' },
|
|
28
|
+
invocationOf({ query: { limit: Choices.LIMIT + 1, ...(where ? { where } : {}) } }),
|
|
29
|
+
);
|
|
30
|
+
const rows = itemsOf<Record<string, unknown>>(answer);
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
choices: rows.slice(0, Choices.LIMIT).map((row) => ({
|
|
34
|
+
value: String(row[reference.key]),
|
|
35
|
+
label: String(row[reference.label] ?? row[reference.key]),
|
|
36
|
+
})),
|
|
37
|
+
more: rows.length > Choices.LIMIT,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
package/src/FormEntity.ts
CHANGED
|
@@ -1,24 +1,14 @@
|
|
|
1
1
|
import { Lifecycle } from '@fougere/schema';
|
|
2
|
-
import { Shapes, lowerFirst, Role, Visibility } from '@fougere/schema';
|
|
2
|
+
import { FieldSet, Shapes, lowerFirst, Role, Visibility } from '@fougere/schema';
|
|
3
3
|
import type { Field, SchemaView, ShapeType, ValidationError } from '@fougere/schema';
|
|
4
|
-
import type { FormField } from './FormField.js';
|
|
4
|
+
import type { FormField, FormReference } from './FormField.js';
|
|
5
|
+
import type { FormErrors, FormFieldName } from './FormRow.js';
|
|
6
|
+
import { rowNameOf } from './RowName.js';
|
|
5
7
|
import type { TableColumn } from './TableColumn.js';
|
|
6
8
|
|
|
7
9
|
/** What an entity class exposes to a form — the schema statics it already has. */
|
|
8
10
|
export type FormEntity = SchemaView;
|
|
9
11
|
|
|
10
|
-
/** The row an entity class builds, which is what its form fills and what `create` answers. */
|
|
11
|
-
export type FormRow<E> = E extends abstract new (...args: never[]) => infer Row ? Row : Record<string, unknown>;
|
|
12
|
-
|
|
13
|
-
/** What the form holds — any field, none of them yet. */
|
|
14
|
-
export type FormValues<E> = Partial<FormRow<E>>;
|
|
15
|
-
|
|
16
|
-
/** A field of that row, by name. */
|
|
17
|
-
export type FormFieldName<E> = keyof FormRow<E> & string;
|
|
18
|
-
|
|
19
|
-
/** One message per refused field. */
|
|
20
|
-
export type FormErrors<E> = Partial<Record<FormFieldName<E>, string>>;
|
|
21
|
-
|
|
22
12
|
/** The literal a field is born with, when it declares one. */
|
|
23
13
|
function defaultOf(field: Field): unknown {
|
|
24
14
|
return Lifecycle.of(field).literal?.value;
|
|
@@ -43,6 +33,7 @@ const CONTROL_BY_TYPE: Record<Exclude<ShapeType, 'text'>, FormField['control']>
|
|
|
43
33
|
};
|
|
44
34
|
|
|
45
35
|
function controlOf(field: Field): FormField['control'] {
|
|
36
|
+
if (Role.of(field).isReference()) return 'reference';
|
|
46
37
|
if (enumOf(field)) return 'select';
|
|
47
38
|
|
|
48
39
|
const type = Shapes.typeOf(field.shape);
|
|
@@ -54,6 +45,20 @@ function controlOf(field: Field): FormField['control'] {
|
|
|
54
45
|
return (format && CONTROL_BY_FORMAT[format]) ?? 'text';
|
|
55
46
|
}
|
|
56
47
|
|
|
48
|
+
/**
|
|
49
|
+
* What a reference field chooses among. The label is the first text the target shows — never a
|
|
50
|
+
* `writeOnly` one, since a choice is read by whoever fills the form — and its key when it shows none,
|
|
51
|
+
* or when the target is known only by name.
|
|
52
|
+
*/
|
|
53
|
+
function referenceOf(field: Field): FormReference | undefined {
|
|
54
|
+
const target = Role.of(field).target;
|
|
55
|
+
if (!target) return undefined;
|
|
56
|
+
const fields = (target as Partial<SchemaView>).getFields?.() ?? {};
|
|
57
|
+
const key = FieldSet.of(fields).primary ?? 'id';
|
|
58
|
+
|
|
59
|
+
return { to: lowerFirst(target.name), key, label: rowNameOf(fields) ?? key };
|
|
60
|
+
}
|
|
61
|
+
|
|
57
62
|
/** A closed set's members, when the shape declares one — `oneOf('draft','live')`. */
|
|
58
63
|
function enumOf(field: Field): readonly (string | number | null)[] | undefined {
|
|
59
64
|
const base = Shapes.of(field.shape).base;
|
|
@@ -98,6 +103,7 @@ export function formFieldsOf<E extends FormEntity>(entity: E, entityKey: string)
|
|
|
98
103
|
const required = Lifecycle.of(f).requiredAtCreate();
|
|
99
104
|
const attrs = attrsOf(f, control, required);
|
|
100
105
|
const members = enumOf(f);
|
|
106
|
+
const reference = referenceOf(f);
|
|
101
107
|
|
|
102
108
|
return {
|
|
103
109
|
name,
|
|
@@ -105,6 +111,7 @@ export function formFieldsOf<E extends FormEntity>(entity: E, entityKey: string)
|
|
|
105
111
|
required,
|
|
106
112
|
...labelOf(name, entityKey),
|
|
107
113
|
...(members ? { options: members.filter((value) => value !== null) } : {}),
|
|
114
|
+
...(reference ? { reference } : {}),
|
|
108
115
|
...(Object.keys(attrs).length ? { attrs } : {}),
|
|
109
116
|
...(defaultOf(f) !== undefined ? { default: defaultOf(f) } : {}),
|
|
110
117
|
};
|
package/src/FormField.ts
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
|
+
/** The entity a reference points at, and which of its fields names one of its rows. */
|
|
2
|
+
export interface FormReference {
|
|
3
|
+
/** Its address — what a call to it names. */
|
|
4
|
+
to: string;
|
|
5
|
+
/** Its primary field: what the reference holds. */
|
|
6
|
+
key: string;
|
|
7
|
+
/** What a choice shows, and what a search looks in. */
|
|
8
|
+
label: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
1
11
|
export interface FormField<Name extends string = string> {
|
|
2
12
|
name: Name;
|
|
3
13
|
/** Rendering hint derived from the shape — the page maps it to widgets. */
|
|
4
|
-
control: 'text' | 'email' | 'url' | 'number' | 'boolean' | 'date' | 'select';
|
|
14
|
+
control: 'text' | 'email' | 'url' | 'number' | 'boolean' | 'date' | 'select' | 'reference';
|
|
5
15
|
required: boolean;
|
|
6
16
|
/** i18n key by convention: `entity.field`. The schema never carries display text. */
|
|
7
17
|
labelKey: string;
|
|
@@ -9,6 +19,8 @@ export interface FormField<Name extends string = string> {
|
|
|
9
19
|
label: string;
|
|
10
20
|
/** Enum values, when control is 'select'. */
|
|
11
21
|
options?: (string | number)[];
|
|
22
|
+
/** What a `reference` control chooses among. */
|
|
23
|
+
reference?: FormReference;
|
|
12
24
|
/**
|
|
13
25
|
* What the browser enforces, under the names it already knows — spread this on the input and the
|
|
14
26
|
* page states no rule of its own.
|
package/src/FormRow.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** The row an entity class builds, which is what its form fills and what `create` answers. */
|
|
2
|
+
export type FormRow<E> = E extends abstract new (...args: never[]) => infer Row ? Row : Record<string, unknown>;
|
|
3
|
+
|
|
4
|
+
/** What the form holds — any field, none of them yet. */
|
|
5
|
+
export type FormValues<E> = Partial<FormRow<E>>;
|
|
6
|
+
|
|
7
|
+
/** A field of that row, by name. */
|
|
8
|
+
export type FormFieldName<E> = keyof FormRow<E> & string;
|
|
9
|
+
|
|
10
|
+
/** One message per refused field. */
|
|
11
|
+
export type FormErrors<E> = Partial<Record<FormFieldName<E>, string>>;
|
package/src/RowName.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Role, Shapes, Visibility, type Fields } from '@fougere/schema';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The field that names a row: the first text a reader may see that is neither its key nor a
|
|
5
|
+
* reference. A choice shows it, a line of a list leads with it, and a search looks in it.
|
|
6
|
+
*/
|
|
7
|
+
export function rowNameOf(fields: Fields): string | undefined {
|
|
8
|
+
const named = Object.entries(Visibility.of(fields).output).find(([, field]) =>
|
|
9
|
+
Shapes.typeOf(field.shape) === 'text' && !Role.of(field).isPrimary() && !Role.of(field).isRelation());
|
|
10
|
+
|
|
11
|
+
return named?.[0];
|
|
12
|
+
}
|
package/src/client/index.ts
CHANGED
|
@@ -37,7 +37,9 @@ export type {
|
|
|
37
37
|
// The form contract is host-independent too, and a form is client code — so it
|
|
38
38
|
// reaches the browser through this subpath rather than through the package root,
|
|
39
39
|
// which carries the boot.
|
|
40
|
-
export { errorsByField, formFieldsOf, payloadOf, tableColumnsOf, type FormEntity
|
|
41
|
-
export { type
|
|
40
|
+
export { errorsByField, formFieldsOf, payloadOf, tableColumnsOf, type FormEntity } from '../FormEntity.js';
|
|
41
|
+
export { type FormRow, type FormValues, type FormErrors, type FormFieldName } from '../FormRow.js';
|
|
42
|
+
export { type FormField, type FormReference } from '../FormField.js';
|
|
43
|
+
export { Choices, type Choice, type Offer } from '../Choices.js';
|
|
42
44
|
export { type TableColumn } from '../TableColumn.js';
|
|
43
45
|
export { sessionViewOf, type SessionView } from '../session.js';
|
package/src/index.ts
CHANGED
|
@@ -13,8 +13,10 @@ export { tableOf } from './RouteMatch.js';
|
|
|
13
13
|
|
|
14
14
|
export { invokeOn, rpcParseError, serveRest, serveRpc, surfaceOf } from './Outcome.js';
|
|
15
15
|
|
|
16
|
-
export { errorsByField, formFieldsOf, payloadOf, tableColumnsOf, type FormEntity
|
|
17
|
-
export { type
|
|
16
|
+
export { errorsByField, formFieldsOf, payloadOf, tableColumnsOf, type FormEntity } from './FormEntity.js';
|
|
17
|
+
export { type FormRow, type FormValues, type FormErrors, type FormFieldName } from './FormRow.js';
|
|
18
|
+
export { type FormField, type FormReference } from './FormField.js';
|
|
19
|
+
export { Choices, type Choice, type Offer } from './Choices.js';
|
|
18
20
|
export { type TableColumn } from './TableColumn.js';
|
|
19
21
|
|
|
20
22
|
export { sessionViewOf, type SessionView } from './session.js';
|