@itrocks/list 0.8.0 → 0.13.4

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/README.md CHANGED
@@ -124,7 +124,7 @@ Both `html()` and `json()` accept an optional application scope as a `SearchType
124
124
  the scope from trusted application state rather than passing request data through directly.
125
125
 
126
126
  For `html()`, the scope is merged after user filters, so it takes precedence if both define
127
- the same property. The resulting search is passed to both `dataSource().search()` and
127
+ the same property. The resulting search is passed to both `dataSource().select()` and
128
128
  `dataSource().count()` without replacing list loading, sorting, pagination, metadata,
129
129
  templates or the configured data source.
130
130
 
@@ -136,3 +136,101 @@ For `json()`, the scope is passed directly to `dataSource().search()`.
136
136
  - Filterable / searchable tables built on top of
137
137
  [@itrocks/table](https://github.com/itrocks-ts/table).
138
138
  - JSON list endpoints consumed by SPA or mobile applications.
139
+
140
+ ## Projected HTML columns
141
+
142
+ HTML lists, feed pages and visible-column requests use the configured `DataSource.select()` implementation. The JSON
143
+ action retains the full-entity `search()` contract. Mandatory scope overrides user filters and applies to projection,
144
+ visible-id reloads and count. Singular columns require a capable projection adapter. Direct collection columns load
145
+ their values through the configured entity reader for the projected visible row ids.
146
+
147
+ Columns are canonical root-relative paths. The list uses public reflection to resolve metadata and supplies each
148
+ projected value to the existing HTML transformer of its final property. Singular object columns request their finite
149
+ representative scalar closure, including nested representative relations. Singular rendering performs no lazy read.
150
+ Absent relationships render empty cells; ignored scalar conversions render `Unavailable`;
151
+ missing representative fields fail explicitly. Plural paths through a relationship and paths with unknown multiplicity
152
+ are refused before SQL.
153
+
154
+ Direct collections display representative values separated by commas. A collection decorated with
155
+ [`@Component()`](https://github.com/itrocks-ts/composition) uses its structured table output. The optional
156
+ `@MultipleOutput('commas' | 'lines')` property decorator from `@itrocks/list/multiple-output` overrides this default.
157
+ `'lines'` displays ordinary collections as a list and keeps component collections as a table. `'commas'` displays
158
+ representative values on one line, including for components. `multipleOutputOf(type, property)` returns the effective
159
+ mode and inherits explicit property metadata. For example:
160
+
161
+ ```ts
162
+ import { MultipleOutput } from '@itrocks/list/multiple-output'
163
+
164
+ class Movie {
165
+ @MultipleOutput('lines')
166
+ genres!: Genre[]
167
+ }
168
+ ```
169
+
170
+ `@itrocks/list/projection` exports `ListColumn`, `representationFields` and `listRepresentationDependsOn`. Custom output
171
+ transformations must declare every input through the `fields(type, ancestors, property?)` resolver when their inputs
172
+ differ from `representativeOf(type)`. Return relative paths, or `undefined` to use the standard metadata. An object
173
+ property/type transformer other than public `storeOutput` is refused before querying unless the resolver declares its
174
+ fields. This check also applies to nested representative relations. A custom global representative callback must provide
175
+ a resolver as part of its composition; arbitrary callback internals are not inferred. Cyclic representative expansion is
176
+ rejected. The hook does not change SQL construction or object reconstruction. Object heading sort expands to
177
+ representative scalar paths followed by object identity; storage adds the root tie-breaker.
178
+
179
+ `listDependsOn({ access })` accepts an asynchronous request-scoped adapter returning `{ root?, authorise(path) }`. The
180
+ framework binds configured metadata roots and checks selected paths, representative fields, filters and ordering.
181
+ Without a configured root the ordinary list remains available, with no selector command. Never retain request state in
182
+ the dependency hook. The configured data source remains responsible for storage and mandatory record scope.
183
+
184
+ `GET /<business-route>/list?column=<path>&rowIds=<comma-separated-identifiers>` uses the HTML action and returns a JSON
185
+ column descriptor with rendered cells keyed by root id. It intersects visible ids with the current filters and mandatory
186
+ scope, makes one projection and performs no count. Invalid selections return 400. Full entity JSON remains available
187
+ through `/list/json`. Column configuration saves use the established `columns` parameter and return 204.
188
+
189
+ The browser entry `@itrocks/list/property-select.js` exports `ListPropertySelect`, with explicit metadata, table
190
+ addition, column persistence and narrow pane ports. The framework mounts it only on compatible lists and disposes it on
191
+ navigation. It reserves pending paths, confirms accepted additions, reveals/highlights duplicates without moving focus,
192
+ and checks row identity before insertion. Failed selection leaves the context intact. It loads only currently visible
193
+ row ids; changing the row view while loading rejects the obsolete response with a retry message. Click requests await
194
+ their data before committing a new column. Desktop drags create immediate placeholder columns through the public
195
+ table external transaction. The header contains each
196
+ label and its filter together, so it uses the table addition plugin's single-header contract.
197
+
198
+ `npm test` runs the server contracts; `test/property-select.test.mjs` covers projection, representation, scope, path
199
+ configuration, filters and ordering. Real framework/browser proof is tracked by the property-select L2-LIST delivery.
200
+
201
+ ## Selector session state
202
+
203
+ The public `@itrocks/list/selector-state` entry exports `SessionStateStore`, `SelectorStateAction`, `selectorStateKey`
204
+ and the composition hook `listStateDependsOn({ transaction })`. The injected function runs an operation against the
205
+ request's authoritative session snapshot and resolves only after durable save. No session is retained by the binding.
206
+ `SessionStateStore` implements the selector's read/write/remove port with revision checks and removal tombstones in
207
+ `session.propertySelect`, separate from `session.list`. The framework supplies the HTTP adapter transaction.
208
+
209
+ `selectorStateKey(request)` uses the router-resolved route, identifying ids and list action. Query and format arguments
210
+ do not affect it. The rendered article supplies this opaque key to the browser. `GET`, `PUT` and `DELETE` at
211
+ `/property-select/state/json` authorise the target list on every request; conflicts return 409 and ended sessions 410.
212
+ Writes echo the opaque `X-Property-Select-Session` token returned by a read. It cannot identify or authenticate a user;
213
+ it prevents a mounted selector from writing its old state into a new session.
214
+
215
+ The public browser `@itrocks/list/selector-state.js` exports `HttpStateStore`, using same-origin credentials and
216
+ no-store requests. `ListSelectorOptions.advice` supplies configured destinations. List column changes refresh used
217
+ paths; only a confirmed new addition feeds the selector's recents. The browser displays recoverable persistence errors.
218
+
219
+ ## External column transport
220
+
221
+ The list consumer supplies immediate cells, authorises admission and loads the current visible ids through the same
222
+ projection/rendering route used for click additions. `validate=1` on a `column` request checks representation and
223
+ permission without selecting records; it prevents unsupported object and nested plural paths from being confirmed
224
+ on drop.
225
+ Admission is independent of the optional values request. A slow or failed values request leaves valid drops accepted;
226
+ failed cells expose `Retry values` without another addition or recent entry.
227
+
228
+ Pending paths and the active drag are reserved before asynchronous work. Used paths retain their committed order
229
+ throughout the drag, and configuration saves exclude provisional cells. New accepted drops feed recents; moves and
230
+ cancellations do not. Row-view changes and view disposal cancel transport. The desktop pane stays open, and capture is
231
+ owned by the selector's surviving source controller. The consumer imports no home implementation, SQL or hydration code.
232
+
233
+ The consumer requests `pane.suspend({ mobileOnly: true })` only when a source gesture has been recognised. The pane
234
+ host owns its responsive breakpoint. The token is restored after acceptance and column persistence, or after
235
+ cancellation/rejection; it never focuses the search field. View disposal removes the registration and invalidates late
236
+ restoration. Custom pane adapters may implement the same optional suspension argument without importing `home`.
package/build.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { build } from '@itrocks/build/build.js';
2
- import { TableColumnReorder } from '@itrocks/table/column-reorder.js';
2
+ import { TableColumnAdd } from '@itrocks/table/column-add.js';
3
3
  import { TableColumnReorderFreeze } from '@itrocks/table/column-reorder-freeze.js';
4
+ import { TableColumnReorder } from '@itrocks/table/column-reorder.js';
4
5
  import { TableFeed } from '@itrocks/table/feed.js';
5
6
  import { TableFreeze } from '@itrocks/table/freeze.js';
6
7
  import { TableFreezeInheritBackground } from '@itrocks/table/freeze/inherit-background.js';
@@ -9,13 +10,20 @@ import { TableLink } from '@itrocks/table/link.js';
9
10
  import { tableByElement } from '@itrocks/table/table.js';
10
11
  import { xTargetCall } from '@itrocks/xtarget/xtarget.js';
11
12
  build('article[data-action="list"] > form > table.objects', table => {
13
+ let saving = Promise.resolve();
12
14
  const saveColumns = () => {
13
- const columns = table.tHead?.rows[0].querySelectorAll(':scope > th[data-property]');
15
+ const columns = table.tHead?.rows[0].querySelectorAll(':scope > th[data-property]:not(.column-add-provisional)');
14
16
  const form = table.closest('form');
15
17
  if (!columns || !form)
16
- return;
18
+ return Promise.resolve();
17
19
  const properties = Array.from(columns, column => encodeURIComponent(column.dataset.property ?? ''));
18
- fetch(form.action + '?columns=' + properties.join(','), { keepalive: true }).catch(error => console.error('Columns save error:', error));
20
+ const url = form.action + '?columns=' + properties.join(',');
21
+ saving = saving.catch(() => { }).then(async () => {
22
+ const response = await fetch(url, { keepalive: true });
23
+ if (!response.ok)
24
+ throw new Error('Unable to save columns');
25
+ });
26
+ return saving;
19
27
  };
20
28
  const tableColumnReorder = new TableColumnReorder({
21
29
  isReorderable: header => header.dataset.property !== undefined,
@@ -30,12 +38,16 @@ build('article[data-action="list"] > form > table.objects', table => {
30
38
  return { element: input, value: input.value };
31
39
  }
32
40
  });
33
- tableByElement(table, {
41
+ const addition = new TableColumnAdd({ identify: header => header.dataset.property });
42
+ const instance = tableByElement(table, {
34
43
  plugins: [
35
- tableColumnReorder, TableColumnReorderFreeze, TableFeed, TableFreeze, TableFreezeInheritBackground,
44
+ tableColumnReorder, addition, TableColumnReorderFreeze, TableFeed, TableFreeze, TableFreezeInheritBackground,
36
45
  TableFreezeInheritBorder, tableLink
37
46
  ]
38
47
  });
48
+ table.dispatchEvent(new CustomEvent('itrocks:list-ready', {
49
+ bubbles: true, detail: { addition, instance, saveColumns }
50
+ }));
39
51
  table.addEventListener('click', event => {
40
52
  const element = event.target;
41
53
  if ((element instanceof HTMLInputElement) || !(element instanceof HTMLElement))
@@ -51,10 +63,11 @@ build('article[data-action="list"] > form > table.objects', table => {
51
63
  });
52
64
  table.tHead?.addEventListener('click', event => {
53
65
  const element = event.target;
54
- if (!(element instanceof HTMLElement))
66
+ if (!(element instanceof HTMLElement) || element.closest('input, button'))
55
67
  return;
56
68
  const cell = element.closest('table, th');
57
- if (!(cell instanceof HTMLTableCellElement) || !cell.dataset.property)
69
+ if (!(cell instanceof HTMLTableCellElement) || !cell.dataset.property
70
+ || cell.dataset.sortable === 'false')
58
71
  return;
59
72
  const form = cell.closest('form');
60
73
  if (!form)
@@ -0,0 +1,10 @@
1
+ import { Request } from '@itrocks/action-request';
2
+ export type ListAccess = {
3
+ authorise(path: string): Promise<void>;
4
+ root?: string;
5
+ };
6
+ export type ListDependencies = {
7
+ access: (request: Request) => Promise<ListAccess>;
8
+ };
9
+ export declare function listDependsOn(overrides: Partial<ListDependencies>): void;
10
+ export declare function listAccess(request: Request): Promise<ListAccess>;
package/cjs/access.js ADDED
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listDependsOn = listDependsOn;
4
+ exports.listAccess = listAccess;
5
+ const dependencies = { access: async () => ({ authorise: async () => { } }) };
6
+ function listDependsOn(overrides) {
7
+ Object.assign(dependencies, overrides);
8
+ }
9
+ function listAccess(request) {
10
+ return dependencies.access(request);
11
+ }
12
+ //# sourceMappingURL=access.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"access.js","sourceRoot":"","sources":["../src/access.ts"],"names":[],"mappings":";;AAUA,sCAGC;AAED,gCAGC;AAVD,MAAM,YAAY,GAAqB,EAAE,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC,EAAE,CAAC,EAAE,CAAA;AAE9F,SAAgB,aAAa,CAAC,SAAoC;IAEjE,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,SAAS,CAAC,CAAA;AACvC,CAAC;AAED,SAAgB,UAAU,CAAC,OAAgB;IAE1C,OAAO,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;AACpC,CAAC"}
package/cjs/columns.d.ts CHANGED
@@ -1,19 +1,27 @@
1
1
  import { Type } from '@itrocks/class-type';
2
- import { ReflectProperty } from '@itrocks/reflect';
3
2
  import { Request } from '@itrocks/request-response';
4
3
  import { Option } from '@itrocks/storage';
4
+ import { ProjectionResult } from '@itrocks/storage';
5
+ import { SearchType } from '@itrocks/storage';
5
6
  import { Parameter } from './parameter';
6
- export declare class Columns<T extends object> extends Parameter implements Iterable<ReflectProperty<T>> {
7
+ import { ListColumn } from './projection';
8
+ export declare class Columns<T extends object> extends Parameter implements Iterable<ListColumn> {
7
9
  private configured;
8
- properties: ReflectProperty<T, keyof T>[];
9
- [Symbol.iterator](): ArrayIterator<ReflectProperty<T, keyof T>>;
10
+ properties: ListColumn[];
11
+ [Symbol.iterator](): ArrayIterator<ListColumn>;
10
12
  getParams(request: Request, type: Type<T>): void;
11
13
  get length(): number;
12
- map(callback: (value: ReflectProperty<T>) => any): any[];
13
- propertiesOf(object: T): ReflectProperty<T, keyof T>[];
14
+ map<R>(callback: (value: ListColumn) => R): R[];
15
+ projected(result: ProjectionResult): Promise<{
16
+ id: import("@itrocks/storage").Identifier;
17
+ properties: {
18
+ outputOptionalContainer: string;
19
+ }[];
20
+ }[]>;
14
21
  readOptions(options?: Option[]): Option[];
15
- remove(propertyName: string): void;
16
- serialize(): (keyof T)[];
17
- unserialize(data: (keyof T)[], type: Type<T>): void;
18
- unshift(property: ReflectProperty<T>): number;
22
+ remove(name: string): void;
23
+ serialize(): string[];
24
+ unserialize(data: string[], type: Type<T>): void;
25
+ unshift(property: ListColumn): number;
19
26
  }
27
+ export declare function projectColumns<T extends object>(type: Type<T>, columns: readonly ListColumn[], search: SearchType<T>, options: Option[]): Promise<ProjectionResult<T>>;
package/cjs/columns.js CHANGED
@@ -1,33 +1,33 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Columns = void 0;
4
+ exports.projectColumns = projectColumns;
4
5
  const list_properties_1 = require("@itrocks/list-properties");
5
- const reflect_1 = require("@itrocks/reflect");
6
+ const storage_1 = require("@itrocks/storage");
7
+ const storage_2 = require("@itrocks/storage");
6
8
  const parameter_1 = require("./parameter");
9
+ const projection_1 = require("./projection");
7
10
  class Columns extends parameter_1.Parameter {
8
11
  configured = false;
9
- properties = new Array;
12
+ properties = [];
10
13
  [Symbol.iterator]() {
11
14
  return this.properties[Symbol.iterator]();
12
15
  }
13
16
  getParams(request, type) {
14
17
  const data = request.data.columns;
15
18
  if (data !== undefined) {
16
- const propertyNames = (Array.isArray(data) ? data : [data]).flatMap(property => (typeof property === 'string') ? (property ? property.split(',') : []) : property);
17
- if (propertyNames.every((property) => typeof property === 'string')) {
18
- const reflected = new list_properties_1.ListReflectClass(type).listProperties();
19
- const reordered = propertyNames.map(propertyName => reflected.find(property => property.name.toString() === propertyName));
20
- const properties = reordered.filter((property) => !!property);
21
- if ((properties.length === propertyNames.length)
22
- && (new Set(properties).size === propertyNames.length)) {
23
- this.configured = true;
19
+ const names = (Array.isArray(data) ? data : [data]).flatMap(property => (typeof property === 'string') ? (property ? property.split(',') : []) : property);
20
+ if (names.every((name) => typeof name === 'string') && new Set(names).size === names.length) {
21
+ try {
22
+ const properties = names.map(name => new projection_1.ListColumn(type, name));
24
23
  this.properties = properties;
24
+ this.configured = true;
25
25
  }
26
+ catch { /* Invalid saved requests preserve the previous configuration. */ }
26
27
  }
27
28
  }
28
- if (!this.configured && !this.length) {
29
- this.properties = new list_properties_1.ListReflectClass(type).listProperties();
30
- }
29
+ if (!this.configured && !this.length)
30
+ this.properties = new list_properties_1.ListReflectClass(type).listProperties().map(property => new projection_1.ListColumn(type, String(property.name)));
31
31
  this.configured = true;
32
32
  }
33
33
  get length() {
@@ -36,30 +36,64 @@ class Columns extends parameter_1.Parameter {
36
36
  map(callback) {
37
37
  return this.properties.map(callback);
38
38
  }
39
- propertiesOf(object) {
40
- return this.properties.map(property => new reflect_1.ReflectProperty(object, property.name));
39
+ async projected(result) {
40
+ if (result.columns.some((column, index) => column.path !== this.properties[index]?.name)
41
+ || result.columns.length !== this.length)
42
+ throw new Error('Projection columns do not match the list');
43
+ return Promise.all(result.rows.map(async (row) => ({
44
+ id: row.rootId,
45
+ properties: await Promise.all(row.cells.map(async (cell, index) => ({
46
+ outputOptionalContainer: await this.properties[index].output(cell)
47
+ })))
48
+ })));
41
49
  }
42
50
  readOptions(options = new Array) {
43
51
  return options;
44
52
  }
45
- remove(propertyName) {
46
- for (const property of this.properties) {
47
- if (property.name !== propertyName)
48
- continue;
49
- this.properties.splice(this.properties.indexOf(property), 1);
50
- return;
51
- }
53
+ remove(name) {
54
+ this.properties = this.properties.filter(property => property.name !== name);
52
55
  }
53
56
  serialize() {
54
57
  return this.properties.map(property => property.name);
55
58
  }
56
59
  unserialize(data, type) {
60
+ this.properties = data.flatMap(name => {
61
+ try {
62
+ return [new projection_1.ListColumn(type, name)];
63
+ }
64
+ catch {
65
+ return [];
66
+ }
67
+ });
57
68
  this.configured = true;
58
- this.properties = data.map(property => new reflect_1.ReflectProperty(type, property));
59
69
  }
60
70
  unshift(property) {
61
71
  return this.properties.unshift(property);
62
72
  }
63
73
  }
64
74
  exports.Columns = Columns;
75
+ async function projectColumns(type, columns, search, options) {
76
+ const singular = columns.filter(column => !column.multiple);
77
+ const result = await (0, storage_1.dataSource)().select(type, singular.map(column => column.selection()), search, options);
78
+ if (!columns.some(column => column.multiple) || !result.rows.length)
79
+ return result;
80
+ const ids = result.rows.map(row => row.rootId);
81
+ const objects = await (0, storage_1.dataSource)().search(type, search, [new storage_2.Identifiers(ids)]);
82
+ const byId = new Map(objects.map(object => [String(object.id), object]));
83
+ return {
84
+ rootType: type,
85
+ columns: columns.map(column => ({ path: column.name, kind: column.multiple ? 'scalar' :
86
+ result.columns[singular.indexOf(column)].kind })),
87
+ rows: await Promise.all(result.rows.map(async (row) => {
88
+ const object = byId.get(String(row.rootId));
89
+ const cells = await Promise.all(columns.map(async (column) => {
90
+ if (!column.multiple)
91
+ return row.cells[singular.indexOf(column)];
92
+ const value = object ? await object[column.name] : [];
93
+ return { kind: 'value', value: value ?? [] };
94
+ }));
95
+ return { rootId: row.rootId, cells };
96
+ }))
97
+ };
98
+ }
65
99
  //# sourceMappingURL=columns.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"columns.js","sourceRoot":"","sources":["../src/columns.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"columns.js","sourceRoot":"","sources":["../src/columns.ts"],"names":[],"mappings":"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"}
package/cjs/feed.js CHANGED
@@ -17,7 +17,7 @@ class Feed extends parameter_1.Parameter {
17
17
  this.limit = xhrInfo.targetHeight
18
18
  ? Math.ceil(xhrInfo.targetHeight / this.lineHeight)
19
19
  : (xhrInfo.visibleRows ?? Feed.DEFAULT_LIMIT);
20
- this.offset = +request.data.offset;
20
+ this.offset = +(request.data.offset ?? Feed.DEFAULT_OFFSET);
21
21
  }
22
22
  readOptions(options = new Array) {
23
23
  options.push(new storage_1.Limit(this.limit, this.offset));
package/cjs/feed.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"feed.js","sourceRoot":"","sources":["../src/feed.ts"],"names":[],"mappings":";;;AACA,8CAA4C;AAE5C,2CAAuC;AAEvC,MAAa,IAAK,SAAQ,qBAAS;IAGlC,MAAM,CAAC,aAAa,GAAS,EAAE,CAAA;IAC/B,MAAM,CAAC,mBAAmB,GAAG,EAAE,CAAA;IAC/B,MAAM,CAAC,cAAc,GAAQ,CAAC,CAAA;IAE9B,KAAK,GAAQ,IAAI,CAAC,aAAa,CAAA;IAC/B,UAAU,GAAG,IAAI,CAAC,mBAAmB,CAAA;IACrC,MAAM,GAAO,IAAI,CAAC,cAAc,CAAA;IAChC,QAAQ,GAAK,SAAS,GAAG,YAAY,CAAA;IAErC,SAAS,CAAC,OAAgB;QAEzB,MAAM,MAAM,GAAI,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,CAAA;QAC3C,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;QAChD,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,YAAY;YAChC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,UAAU,CAAC;YACpD,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,IAAI,IAAI,CAAC,aAAa,CAAC,CAAA;QAC7C,IAAI,CAAC,MAAM,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAA;IACnC,CAAC;IAED,WAAW,CAAC,UAAU,IAAI,KAAa;QAEtC,OAAO,CAAC,IAAI,CAAC,IAAI,eAAK,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;QAChD,OAAO,OAAO,CAAA;IACf,CAAC;;AA1BF,oBA4BC"}
1
+ {"version":3,"file":"feed.js","sourceRoot":"","sources":["../src/feed.ts"],"names":[],"mappings":";;;AACA,8CAA4C;AAE5C,2CAAuC;AAEvC,MAAa,IAAK,SAAQ,qBAAS;IAGlC,MAAM,CAAC,aAAa,GAAS,EAAE,CAAA;IAC/B,MAAM,CAAC,mBAAmB,GAAG,EAAE,CAAA;IAC/B,MAAM,CAAC,cAAc,GAAQ,CAAC,CAAA;IAE9B,KAAK,GAAQ,IAAI,CAAC,aAAa,CAAA;IAC/B,UAAU,GAAG,IAAI,CAAC,mBAAmB,CAAA;IACrC,MAAM,GAAO,IAAI,CAAC,cAAc,CAAA;IAChC,QAAQ,GAAK,SAAS,GAAG,YAAY,CAAA;IAErC,SAAS,CAAC,OAAgB;QAEzB,MAAM,MAAM,GAAI,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,CAAA;QAC3C,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;QAChD,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,YAAY;YAChC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,UAAU,CAAC;YACpD,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,IAAI,IAAI,CAAC,aAAa,CAAC,CAAA;QAC7C,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,cAAc,CAAC,CAAA;IAC5D,CAAC;IAED,WAAW,CAAC,UAAU,IAAI,KAAa;QAEtC,OAAO,CAAC,IAAI,CAAC,IAAI,eAAK,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;QAChD,OAAO,OAAO,CAAA;IACf,CAAC;;AA1BF,oBA4BC"}
package/cjs/filter.js CHANGED
@@ -13,11 +13,30 @@ class Filter extends parameter_1.Parameter {
13
13
  for (const property of this.columns.map(property => property.name)) {
14
14
  this.search[property] ??= '';
15
15
  }
16
- for (const [property, search] of Object.entries(request.data.search ?? {})) {
17
- if (!(property in this.search))
18
- continue;
19
- this.search[property] = search;
20
- }
16
+ if ('reset' in request.data)
17
+ this.search = Object.fromEntries(Object.keys(this.search).map(path => [path, '']));
18
+ const apply = (value, prefix = '') => {
19
+ if (!value || typeof value !== 'object' || Array.isArray(value))
20
+ return;
21
+ for (const [name, child] of Object.entries(value)) {
22
+ let decoded;
23
+ try {
24
+ decoded = decodeURIComponent(name);
25
+ }
26
+ catch {
27
+ continue;
28
+ }
29
+ const path = prefix ? `${prefix}.${decoded}` : decoded;
30
+ if (typeof child === 'string' && Object.hasOwn(this.search, path))
31
+ this.search[path] = child;
32
+ else
33
+ apply(child, path);
34
+ }
35
+ };
36
+ apply(request.data.search);
37
+ for (const [name, value] of Object.entries(request.data))
38
+ if (name.startsWith('search.') && typeof value === 'string')
39
+ apply({ [name.slice(7)]: value });
21
40
  }
22
41
  readOptions(options = new Array) {
23
42
  return options;
package/cjs/filter.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"filter.js","sourceRoot":"","sources":["../src/filter.ts"],"names":[],"mappings":";;;AAIA,2CAAuC;AAIvC,MAAa,MAAyB,SAAQ,qBAAS;IAKnC;IAFnB,MAAM,GAAW,EAAE,CAAA;IAEnB,YAAmB,OAAmB;QAErC,KAAK,EAAE,CAAA;QAFW,YAAO,GAAP,OAAO,CAAY;IAGtC,CAAC;IAED,SAAS,CAAC,OAAgB;QAEzB,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YACpE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAA;QAC7B,CAAC;QACD,KAAK,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,CAAC;YAC5E,IAAI,CAAC,CAAC,QAAQ,IAAI,IAAI,CAAC,MAAM,CAAC;gBAAE,SAAQ;YACxC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,MAAM,CAAA;QAC/B,CAAC;IACF,CAAC;IAED,WAAW,CAAC,UAAU,IAAI,KAAa;QAEtC,OAAO,OAAO,CAAA;IACf,CAAC;IAED,YAAY,CAAC,IAAU;QAEtB,MAAM,MAAM,GAAW,EAAE,CAAA;QACzB,KAAK,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;YAC7D,IAAI,KAAK,KAAK,EAAE;gBAAE,SAAQ;YAC1B,MAAM,CAAC,QAAQ,CAAC,GAAG,KAAK,CAAA;QACzB,CAAC;QACD,OAAO,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAA;IACrD,CAAC;IAED,SAAS;QAER,OAAO,IAAI,CAAC,MAAM,CAAA;IACnB,CAAC;IAED,WAAW,CAAC,IAAY;QAEvB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAA;IACnB,CAAC;CAED;AA9CD,wBA8CC"}
1
+ {"version":3,"file":"filter.js","sourceRoot":"","sources":["../src/filter.ts"],"names":[],"mappings":"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"}
package/cjs/list.d.ts CHANGED
@@ -5,11 +5,14 @@ import { Columns } from './columns';
5
5
  import { Feed } from './feed';
6
6
  import { Filter } from './filter';
7
7
  import { Sort } from './sort';
8
+ export { listDependsOn } from './access';
9
+ export type { ListAccess, ListDependencies } from './access';
8
10
  export declare class List<T extends object = object> extends Action<T> {
9
11
  columns: Columns<object>;
10
12
  feed: Feed;
11
13
  filter: Filter<object>;
12
14
  sort: Sort<object>;
13
- html(request: Request<T>, scope?: SearchType<T>): Promise<import("@itrocks/core-responses").HtmlResponse>;
15
+ html(request: Request<T>, scope?: SearchType<T>): Promise<import("@itrocks/core-responses").JsonResponse | import("@itrocks/core-responses").HtmlResponse>;
14
16
  json(request: Request<T>, scope?: SearchType<T>): Promise<import("@itrocks/core-responses").JsonResponse>;
15
17
  }
18
+ export default List;
package/cjs/list.html CHANGED
@@ -14,9 +14,11 @@
14
14
  <!--BEGIN-->
15
15
 
16
16
  <!--#main-->
17
- <article data-action="list" data-class="{type.name}" data-route="{type.@route}">
17
+ <article data-action="list" data-class="{type.name}" data-route="{type.@route}" data-property-root="{?root}"
18
+ data-selector-key="{?selectorKey}">
18
19
  <header>
19
20
  <h2>{type.@output.ucFirst} list</h2>
21
+ <button class="add-column" hidden type="button" aria-label="Add column">+</button>
20
22
  <section class="summary">{count} elements</section>
21
23
  <ul class="general actions">
22
24
  <!--generalActions-->
@@ -28,15 +30,13 @@
28
30
  <table class="objects" data-feed-fields="search.*" data-link="{type.@route}">
29
31
  <thead>
30
32
  <tr class="properties">
31
- <th class="select" data-freeze><input name="select" type="checkbox" value="all"></th>
33
+ <th class="select" data-freeze><input name="select" type="checkbox" value="all">
34
+ <input name="reset" title="reset search filters" type="submit" value="reset"></th>
32
35
  <!--columns-->
33
- <th data-property="{name}" data-reverse="{?reverse}" data-sort="{?sort}">{@display}</th>
34
- <!--end-->
35
- </tr>
36
- <tr class="search">
37
- <th><input name="reset" title="reset search filters" type="submit" value="reset"></th>
38
- <!--columns-->
39
- <th><input name="search.{name.toField}" value="{?.search.{name.toField}}"></th>
36
+ <th data-property="{name}" data-reverse="{?reverse}" data-sort="{?sort}"
37
+ data-sortable="{sortable}">{label}
38
+ <input aria-label="Filter {label}" data-property-filter="{name}" name="search.{field}" value="{search}">
39
+ </th>
40
40
  <!--end-->
41
41
  </tr>
42
42
  </thead>
package/cjs/list.js CHANGED
@@ -34,19 +34,27 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
34
34
  return useValue ? value : void 0;
35
35
  };
36
36
  Object.defineProperty(exports, "__esModule", { value: true });
37
- exports.List = void 0;
37
+ exports.List = exports.listDependsOn = void 0;
38
38
  const action_1 = require("@itrocks/action");
39
39
  const action_2 = require("@itrocks/action");
40
40
  const action_3 = require("@itrocks/action");
41
41
  const route_1 = require("@itrocks/route");
42
42
  const route_2 = require("@itrocks/route");
43
43
  const storage_1 = require("@itrocks/storage");
44
+ const storage_2 = require("@itrocks/storage");
45
+ const storage_3 = require("@itrocks/storage");
46
+ const access_1 = require("./access");
44
47
  const columns_1 = require("./columns");
48
+ const columns_2 = require("./columns");
45
49
  const feed_1 = require("./feed");
46
50
  const filter_1 = require("./filter");
47
51
  const persist_1 = require("./persist");
48
52
  const persist_2 = require("./persist");
53
+ const projection_1 = require("./projection");
54
+ const selector_state_1 = require("./selector-state");
49
55
  const sort_1 = require("./sort");
56
+ var access_2 = require("./access");
57
+ Object.defineProperty(exports, "listDependsOn", { enumerable: true, get: function () { return access_2.listDependsOn; } });
50
58
  let List = (() => {
51
59
  let _classDecorators = [(0, action_3.Need)('Store', 'new'), (0, route_1.Route)('/list')];
52
60
  let _classDescriptor;
@@ -74,20 +82,56 @@ let List = (() => {
74
82
  const type = request.type;
75
83
  (0, persist_1.load)(this, request.request, type);
76
84
  columns.getParams(request.request, type);
77
- if (request.request.data.columns) {
85
+ const access = await (0, access_1.listAccess)(request);
86
+ for (const column of columns) {
87
+ await access.authorise(column.name);
88
+ for (const field of column.fields ?? [])
89
+ await access.authorise(`${column.name}.${field}`);
90
+ }
91
+ if (request.request.data.columns !== undefined) {
78
92
  (0, persist_2.save)(this, request.request, type);
79
93
  return this.htmlResponse('', 204);
80
94
  }
81
95
  feed.getParams(request.request);
82
96
  filter.getParams(request.request);
97
+ for (const column of columns)
98
+ column.search = filter.search[column.name] ?? '';
83
99
  sort.getParams(request.request, type);
84
100
  (0, persist_2.save)(this, request.request, type);
85
101
  const options = feed.readOptions(filter.readOptions(sort.readOptions()));
86
102
  const search = Object.assign(filter.searchObject(type), scope);
87
- const objects = (await (0, storage_1.dataSource)().search(type, search, options)).map(object => ({
88
- id: object.id,
89
- properties: columns.propertiesOf(object)
90
- }));
103
+ for (const path of Object.keys(search))
104
+ await access.authorise(path);
105
+ for (const path of Object.keys(sort.sorted ?? {}))
106
+ await access.authorise(path);
107
+ if (typeof request.request.data.column === 'string') {
108
+ try {
109
+ const path = request.request.data.column;
110
+ await access.authorise(path);
111
+ const column = new projection_1.ListColumn(type, path);
112
+ for (const field of column.fields ?? [])
113
+ await access.authorise(`${path}.${field}`);
114
+ if (request.request.data.validate === '1')
115
+ return this.jsonResponse({ label: column.label, path, sortable: column.sortable });
116
+ const ids = request.request.data.rowIds;
117
+ if (typeof ids !== 'string')
118
+ throw new Error('Visible row identities are required');
119
+ const result = await (0, columns_2.projectColumns)(type, [column], search, [new storage_2.Identifiers(ids ? ids.split(',') : [])]);
120
+ return this.jsonResponse({
121
+ label: column.label, path, sortable: column.sortable,
122
+ rows: await Promise.all(result.rows.map(async (row) => ({
123
+ id: row.rootId, html: await column.output(row.cells[0])
124
+ })))
125
+ });
126
+ }
127
+ catch (error) {
128
+ const message = error instanceof storage_3.SelectionError && error.code === 'multiple-values'
129
+ ? 'This path has multiple values and cannot be displayed as a column.'
130
+ : error instanceof Error ? error.message : 'Column unavailable';
131
+ return this.jsonResponse({ error: message }, 400);
132
+ }
133
+ }
134
+ const objects = await columns.projected(await (0, columns_2.projectColumns)(type, [...columns], search, options));
91
135
  if (feed.offset) {
92
136
  return this.htmlTemplateResponse({ objects, type }, request, feed.template);
93
137
  }
@@ -99,7 +143,8 @@ let List = (() => {
99
143
  const actions = (action.need === 'object') ? selectionActions : generalActions;
100
144
  actions.push(action);
101
145
  }
102
- return this.htmlTemplateResponse({ columns, count, generalActions, objects, search: filter.search, selectionActions, type }, request, __dirname + '/list.html');
146
+ return this.htmlTemplateResponse({ columns, count, generalActions, objects, root: access.root, search: filter.search,
147
+ selectionActions, selectorKey: (0, selector_state_1.selectorStateKey)(request), type }, request, __dirname + '/list.html');
103
148
  }
104
149
  async json(request, scope) {
105
150
  const objects = await (0, storage_1.dataSource)().search(request.type, scope);
@@ -109,4 +154,6 @@ let List = (() => {
109
154
  return List = _classThis;
110
155
  })();
111
156
  exports.List = List;
157
+ // Route resolution must select the action alongside its public dependency hooks.
158
+ exports.default = List;
112
159
  //# sourceMappingURL=list.js.map