@fougere/app 0.16.3-alpha.0 → 0.16.4-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.
@@ -0,0 +1,15 @@
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
+ /** The rows a reference field offers — a handful, and a search to reach the others. */
9
+ export declare class Choices {
10
+ /** Enough to choose among at a glance; the rest is what a search is for. */
11
+ static readonly LIMIT = 20;
12
+ /** The first rows of the target, or those whose label contains `text`. */
13
+ static of(fetcher: Fetcher, reference: FormReference, text?: string): Promise<Choice[]>;
14
+ }
15
+ //# 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,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,MAAM,EAAE,CAAC,CAYxF;CACF"}
@@ -0,0 +1,16 @@
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, ...(where ? { where } : {}) } }));
10
+ return itemsOf(answer).map((row) => ({
11
+ value: String(row[reference.key]),
12
+ label: String(row[reference.label] ?? row[reference.key]),
13
+ }));
14
+ }
15
+ }
16
+ //# 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;AAUnE,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,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAC/E,CAAC;QAEF,OAAO,OAAO,CAA0B,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YAC5D,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YACjC,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;SAC1D,CAAC,CAAC,CAAC;IACN,CAAC;CACF"}
@@ -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. */
@@ -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,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,kFAAkF;AAClF,MAAM,MAAM,UAAU,GAAG,UAAU,CAAC;AAEpC,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;AAyEtE,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,CAoB9G;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"}
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"}
@@ -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
  };
@@ -1 +1 @@
1
- {"version":3,"file":"FormEntity.js","sourceRoot":"","sources":["../src/FormEntity.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"FormEntity.js","sourceRoot":"","sources":["../src/FormEntity.ts"],"names":[],"mappings":"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"}
@@ -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.
@@ -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;IAC7E,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;;;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"}
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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=FormRow.js.map
@@ -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"}
@@ -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"}
@@ -0,0 +1,11 @@
1
+ import { type FormEntity } from './FormEntity.js';
2
+ import type { TableColumn } from './TableColumn.js';
3
+ /** What a line of a list shows of a row: the field that names it, and the facts read at a glance. */
4
+ export interface Summary {
5
+ name?: TableColumn;
6
+ facts: TableColumn[];
7
+ }
8
+ export declare function summaryOf(entity: FormEntity, entityKey: string): Summary;
9
+ /** One column of a row as a line prints it: a date without its hour, a true boolean by its label, a false one not at all. */
10
+ export declare function cellOf(column: TableColumn, row: object): string;
11
+ //# sourceMappingURL=Summary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Summary.d.ts","sourceRoot":"","sources":["../src/Summary.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,qGAAqG;AACrG,MAAM,WAAW,OAAO;IACtB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;AAKD,wBAAgB,SAAS,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAYxE;AAED,6HAA6H;AAC7H,wBAAgB,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAO/D"}
@@ -0,0 +1,28 @@
1
+ import { Role, Shapes } from '@fougere/schema';
2
+ import { tableColumnsOf } from './FormEntity.js';
3
+ import { rowNameOf } from './RowName.js';
4
+ /** What reads at a glance. A long text, a key, a reference or a document does not. */
5
+ const AT_A_GLANCE = new Set(['choice', 'boolean', 'number', 'integer', 'date']);
6
+ export function summaryOf(entity, entityKey) {
7
+ const fields = entity.getFields();
8
+ const columns = tableColumnsOf(entity, entityKey);
9
+ const name = columns.find((column) => column.name === rowNameOf(fields));
10
+ const facts = columns.filter((column) => {
11
+ const field = fields[column.name];
12
+ const type = Shapes.typeOf(field.shape);
13
+ return column !== name && type !== undefined && AT_A_GLANCE.has(type) && !Role.of(field).isRelation();
14
+ });
15
+ return { ...(name ? { name } : {}), facts };
16
+ }
17
+ /** One column of a row as a line prints it: a date without its hour, a true boolean by its label, a false one not at all. */
18
+ export function cellOf(column, row) {
19
+ const value = row[column.name];
20
+ if (value === undefined || value === null || value === false)
21
+ return '';
22
+ if (value === true)
23
+ return column.label;
24
+ if (column.render === 'date')
25
+ return new Date(value).toLocaleDateString();
26
+ return String(value);
27
+ }
28
+ //# sourceMappingURL=Summary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Summary.js","sourceRoot":"","sources":["../src/Summary.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAkB,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAmB,MAAM,iBAAiB,CAAC;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AASzC,sFAAsF;AACtF,MAAM,WAAW,GAAG,IAAI,GAAG,CAAY,CAAC,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;AAE3F,MAAM,UAAU,SAAS,CAAC,MAAkB,EAAE,SAAiB;IAC7D,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,EAAE,CAAC;IAClC,MAAM,OAAO,GAAG,cAAc,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAClD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;IACzE,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE;QACtC,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAE,CAAC;QACnC,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAExC,OAAO,MAAM,KAAK,IAAI,IAAI,IAAI,KAAK,SAAS,IAAI,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,UAAU,EAAE,CAAC;IACxG,CAAC,CAAC,CAAC;IAEH,OAAO,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC;AAC9C,CAAC;AAED,6HAA6H;AAC7H,MAAM,UAAU,MAAM,CAAC,MAAmB,EAAE,GAAW;IACrD,MAAM,KAAK,GAAI,GAA+B,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC5D,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,KAAK;QAAE,OAAO,EAAE,CAAC;IACxE,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC,KAAK,CAAC;IACxC,IAAI,MAAM,CAAC,MAAM,KAAK,MAAM;QAAE,OAAO,IAAI,IAAI,CAAC,KAAsB,CAAC,CAAC,kBAAkB,EAAE,CAAC;IAE3F,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC"}
@@ -3,8 +3,11 @@ 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, type FormRow, type FormValues, type FormErrors, type FormFieldName } from '../FormEntity.js';
7
- export { type FormField } from '../FormField.js';
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 } from '../Choices.js';
10
+ export { cellOf, summaryOf, type Summary } from '../Summary.js';
8
11
  export { type TableColumn } from '../TableColumn.js';
9
12
  export { sessionViewOf, type SessionView } from '../session.js';
10
13
  //# 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,kBAAkB,CAAC;AAC/K,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC"}
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,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,OAAO,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC"}
@@ -3,5 +3,7 @@ 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';
7
+ export { cellOf, summaryOf } from '../Summary.js';
6
8
  export { sessionViewOf } from '../session.js';
7
9
  //# sourceMappingURL=index.js.map
@@ -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,EAAuF,MAAM,kBAAkB,CAAC;AAG/K,OAAO,EAAE,aAAa,EAAoB,MAAM,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,EAAe,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,SAAS,EAAgB,MAAM,eAAe,CAAC;AAEhE,OAAO,EAAE,aAAa,EAAoB,MAAM,eAAe,CAAC"}
package/dist/index.d.ts CHANGED
@@ -3,8 +3,11 @@ 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, type FormRow, type FormValues, type FormErrors, type FormFieldName } from './FormEntity.js';
7
- export { type FormField } from './FormField.js';
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 } from './Choices.js';
10
+ export { cellOf, summaryOf, type Summary } from './Summary.js';
8
11
  export { type TableColumn } from './TableColumn.js';
9
12
  export { sessionViewOf, type SessionView } from './session.js';
10
13
  export { stateFor } from './state.js';
@@ -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,iBAAiB,CAAC;AAC9K,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,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"}
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,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,OAAO,EAAE,MAAM,cAAc,CAAC;AAC/D,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,8 @@ 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';
8
+ export { cellOf, summaryOf } from './Summary.js';
7
9
  export { sessionViewOf } from './session.js';
8
10
  export { stateFor } from './state.js';
9
11
  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,EAAuF,MAAM,iBAAiB,CAAC;AAI9K,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"}
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,EAAe,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,SAAS,EAAgB,MAAM,cAAc,CAAC;AAG/D,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-alpha.0",
3
+ "version": "0.16.4-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.3-alpha.0",
45
- "@fougere/defaults": "0.16.3-alpha.0",
46
- "@fougere/schema": "0.16.3-alpha.0",
47
- "@fougere/adapter-rest": "0.16.3-alpha.0",
48
- "@fougere/transport-http": "0.16.3-alpha.0",
44
+ "@fougere/core": "0.16.4-alpha.0",
45
+ "@fougere/defaults": "0.16.4-alpha.0",
46
+ "@fougere/schema": "0.16.4-alpha.0",
47
+ "@fougere/adapter-rest": "0.16.4-alpha.0",
48
+ "@fougere/transport-http": "0.16.4-alpha.0",
49
49
  "jiti": "^2.6.1",
50
- "@fougere/http": "0.16.3-alpha.0",
51
- "@fougere/container": "0.16.3-alpha.0",
52
- "@fougere/adapter-memory": "0.16.3-alpha.0",
53
- "@fougere/compiler": "0.16.3-alpha.0"
50
+ "@fougere/http": "0.16.4-alpha.0",
51
+ "@fougere/container": "0.16.4-alpha.0",
52
+ "@fougere/adapter-memory": "0.16.4-alpha.0",
53
+ "@fougere/compiler": "0.16.4-alpha.0"
54
54
  },
55
55
  "devDependencies": {
56
56
  "vitest": "^4.1.0",
57
- "@fougere/adapter-graphql": "0.16.3-alpha.0"
57
+ "@fougere/adapter-graphql": "0.16.4-alpha.0"
58
58
  },
59
59
  "publishConfig": {
60
60
  "access": "public"
61
61
  },
62
62
  "peerDependencies": {
63
- "@fougere/adapter-graphql": "^0.16.3-alpha.0"
63
+ "@fougere/adapter-graphql": "^0.16.4-alpha.0"
64
64
  },
65
65
  "peerDependenciesMeta": {
66
66
  "@fougere/adapter-graphql": {
package/src/Choices.ts ADDED
@@ -0,0 +1,30 @@
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
+ /** The rows a reference field offers — a handful, and a search to reach the others. */
12
+ export class Choices {
13
+ /** Enough to choose among at a glance; the rest is what a search is for. */
14
+ static readonly LIMIT = 20;
15
+
16
+ /** The first rows of the target, or those whose label contains `text`. */
17
+ static async of(fetcher: Fetcher, reference: FormReference, text = ''): Promise<Choice[]> {
18
+ const where = text ? { [reference.label]: { contains: text } } : undefined;
19
+ const answer = await sendCall(
20
+ fetcher,
21
+ { entity: reference.to, op: 'list' },
22
+ invocationOf({ query: { limit: Choices.LIMIT, ...(where ? { where } : {}) } }),
23
+ );
24
+
25
+ return itemsOf<Record<string, unknown>>(answer).map((row) => ({
26
+ value: String(row[reference.key]),
27
+ label: String(row[reference.label] ?? row[reference.key]),
28
+ }));
29
+ }
30
+ }
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/Summary.ts ADDED
@@ -0,0 +1,37 @@
1
+ import { Role, Shapes, type ShapeType } from '@fougere/schema';
2
+ import { tableColumnsOf, type FormEntity } from './FormEntity.js';
3
+ import { rowNameOf } from './RowName.js';
4
+ import type { TableColumn } from './TableColumn.js';
5
+
6
+ /** What a line of a list shows of a row: the field that names it, and the facts read at a glance. */
7
+ export interface Summary {
8
+ name?: TableColumn;
9
+ facts: TableColumn[];
10
+ }
11
+
12
+ /** What reads at a glance. A long text, a key, a reference or a document does not. */
13
+ const AT_A_GLANCE = new Set<ShapeType>(['choice', 'boolean', 'number', 'integer', 'date']);
14
+
15
+ export function summaryOf(entity: FormEntity, entityKey: string): Summary {
16
+ const fields = entity.getFields();
17
+ const columns = tableColumnsOf(entity, entityKey);
18
+ const name = columns.find((column) => column.name === rowNameOf(fields));
19
+ const facts = columns.filter((column) => {
20
+ const field = fields[column.name]!;
21
+ const type = Shapes.typeOf(field.shape);
22
+
23
+ return column !== name && type !== undefined && AT_A_GLANCE.has(type) && !Role.of(field).isRelation();
24
+ });
25
+
26
+ return { ...(name ? { name } : {}), facts };
27
+ }
28
+
29
+ /** One column of a row as a line prints it: a date without its hour, a true boolean by its label, a false one not at all. */
30
+ export function cellOf(column: TableColumn, row: object): string {
31
+ const value = (row as Record<string, unknown>)[column.name];
32
+ if (value === undefined || value === null || value === false) return '';
33
+ if (value === true) return column.label;
34
+ if (column.render === 'date') return new Date(value as string | Date).toLocaleDateString();
35
+
36
+ return String(value);
37
+ }
@@ -37,7 +37,10 @@ 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, type FormRow, type FormValues, type FormErrors, type FormFieldName } from '../FormEntity.js';
41
- export { type FormField } from '../FormField.js';
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 } from '../Choices.js';
44
+ export { cellOf, summaryOf, type Summary } from '../Summary.js';
42
45
  export { type TableColumn } from '../TableColumn.js';
43
46
  export { sessionViewOf, type SessionView } from '../session.js';
package/src/index.ts CHANGED
@@ -13,8 +13,11 @@ 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, type FormRow, type FormValues, type FormErrors, type FormFieldName } from './FormEntity.js';
17
- export { type FormField } from './FormField.js';
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 } from './Choices.js';
20
+ export { cellOf, summaryOf, type Summary } from './Summary.js';
18
21
  export { type TableColumn } from './TableColumn.js';
19
22
 
20
23
  export { sessionViewOf, type SessionView } from './session.js';