@happyvertical/smrt-content 0.43.4 → 0.43.6
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/AGENTS.md +9 -86
- package/agents/content-list.md +869 -0
- package/dist/content-query.d.ts +310 -0
- package/dist/content-query.d.ts.map +1 -0
- package/dist/contents.d.ts +22 -0
- package/dist/contents.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +672 -4
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +22 -2
- package/dist/smrt-knowledge.json +38 -5
- package/dist/svelte/components/ContentList.svelte +941 -30
- package/dist/svelte/components/ContentList.svelte.d.ts +44 -1
- package/dist/svelte/components/ContentList.svelte.d.ts.map +1 -1
- package/dist/svelte/content-list-controller.d.ts +98 -1
- package/dist/svelte/content-list-controller.d.ts.map +1 -1
- package/dist/svelte/content-list-controller.js +290 -19
- package/dist/svelte/content-list-query.d.ts +498 -0
- package/dist/svelte/content-list-query.d.ts.map +1 -0
- package/dist/svelte/content-list-query.js +1294 -0
- package/dist/svelte/content-list-saved-views.d.ts +172 -0
- package/dist/svelte/content-list-saved-views.d.ts.map +1 -0
- package/dist/svelte/content-list-saved-views.js +298 -0
- package/dist/svelte/content-list-url-state.d.ts +211 -0
- package/dist/svelte/content-list-url-state.d.ts.map +1 -0
- package/dist/svelte/content-list-url-state.js +856 -0
- package/dist/svelte/i18n.contribution.d.ts +24 -0
- package/dist/svelte/i18n.contribution.d.ts.map +1 -1
- package/dist/svelte/i18n.contribution.js +26 -0
- package/dist/svelte/index.d.ts +5 -1
- package/dist/svelte/index.d.ts.map +1 -1
- package/dist/svelte/index.js +8 -1
- package/package.json +16 -15
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shareable URL state for ContentList (#2452).
|
|
3
|
+
*
|
|
4
|
+
* A content list view is a link: an operator narrows the list, copies the
|
|
5
|
+
* address bar, and a colleague opens the same result. That makes the query
|
|
6
|
+
* string an *untrusted* input — it arrives from whoever sent the link, not from
|
|
7
|
+
* the operator's own session — so every restored value is re-derived here
|
|
8
|
+
* against the adapter's published column and operator vocabulary rather than
|
|
9
|
+
* trusted as written.
|
|
10
|
+
*
|
|
11
|
+
* The module owns two things:
|
|
12
|
+
*
|
|
13
|
+
* 1. A compact, human-legible parameter shape (`q`, `type`, `status`, `sort`,
|
|
14
|
+
* `page`, `size`, and `<column>.<operator>` for the richer operators). A
|
|
15
|
+
* base64 blob would round-trip just as well but would be unreadable, and an
|
|
16
|
+
* unreadable link is one nobody can sanity-check before sending it.
|
|
17
|
+
* 2. `sanitizeContentListViewState`, the single validator shared with the
|
|
18
|
+
* saved-views module (`content-list-saved-views.ts`), so a crafted URL and a
|
|
19
|
+
* tampered stored view are held to exactly the same allowlist.
|
|
20
|
+
*
|
|
21
|
+
* ## What may be restored
|
|
22
|
+
*
|
|
23
|
+
* | Aspect | Allowlist |
|
|
24
|
+
* |--------|-----------|
|
|
25
|
+
* | filters, sorting | `CONTENT_LIST_VISIBLE_COLUMN_IDS` — the columns the surface descriptor publishes with `filter`/`sort` capability |
|
|
26
|
+
* | projection (order, visibility, widths, pinning) | `CONTENT_LIST_TABLE_COLUMN_IDS`, and a hidden column can never be forced visible |
|
|
27
|
+
* | filter operators | `CONTENT_LIST_FILTER_OPERATORS` |
|
|
28
|
+
* | filter values | strings only, normalized by `normalizeContentListFilterValue` |
|
|
29
|
+
*
|
|
30
|
+
* The search-only `description` column, the structural `select`/`actions`
|
|
31
|
+
* columns, and any column id the adapter does not publish are dropped from
|
|
32
|
+
* filters and sorting. Dropping — never throwing — is deliberate: a stale link
|
|
33
|
+
* or an out-of-date saved view must still open the list, minus the parts that
|
|
34
|
+
* are no longer meaningful.
|
|
35
|
+
*
|
|
36
|
+
* ## What is deliberately NOT URL state
|
|
37
|
+
*
|
|
38
|
+
* Selection and expansion are excluded, and `sanitizeContentListViewState`
|
|
39
|
+
* never emits them either. A shared link must not carry another operator's
|
|
40
|
+
* selection: the recipient would inherit a checked set they never chose, and
|
|
41
|
+
* the very next bulk action — delete included — would run against it. Row ids
|
|
42
|
+
* are also the one part of view state that leaks data (which specific contents
|
|
43
|
+
* someone had singled out) into a URL that gets pasted into chat and tickets.
|
|
44
|
+
*/
|
|
45
|
+
import type { DataTableController, DataTableTransition, DataTableViewState } from '@happyvertical/smrt-ui/data';
|
|
46
|
+
/** Query-string parameter carrying the free-text search. */
|
|
47
|
+
export declare const CONTENT_LIST_SEARCH_PARAM = "q";
|
|
48
|
+
/** Query-string parameter carrying the ordered sort rules. */
|
|
49
|
+
export declare const CONTENT_LIST_SORT_PARAM = "sort";
|
|
50
|
+
/** Query-string parameter carrying the 1-based page. */
|
|
51
|
+
export declare const CONTENT_LIST_PAGE_PARAM = "page";
|
|
52
|
+
/** Query-string parameter carrying the page size. */
|
|
53
|
+
export declare const CONTENT_LIST_PAGE_SIZE_PARAM = "size";
|
|
54
|
+
/**
|
|
55
|
+
* Parameter names this module owns. A column may not shadow one of them; the
|
|
56
|
+
* assertion below keeps that true if a future column is ever named `q` or
|
|
57
|
+
* `page`.
|
|
58
|
+
*/
|
|
59
|
+
export declare const CONTENT_LIST_RESERVED_PARAMS: readonly ["q", "sort", "page", "size"];
|
|
60
|
+
/**
|
|
61
|
+
* Columns a restored filter or sort rule may address: exactly the columns the
|
|
62
|
+
* surface descriptor publishes with `filter` and `sort` capability.
|
|
63
|
+
*/
|
|
64
|
+
export declare const CONTENT_LIST_QUERYABLE_COLUMN_IDS: readonly ["type", "title", "author", "status", "state", "publish", "updated", "site"];
|
|
65
|
+
/**
|
|
66
|
+
* Operators a restored filter may use. This is the full DataTable operator
|
|
67
|
+
* vocabulary the adapter's evaluator implements — narrower than "anything that
|
|
68
|
+
* parses", so an unrecognized operator can never reach the query layer.
|
|
69
|
+
*/
|
|
70
|
+
export declare const CONTENT_LIST_FILTER_OPERATORS: readonly ["equals", "notEquals", "contains", "notContains", "startsWith", "endsWith", "in", "notIn", "gt", "gte", "lt", "lte", "isNull", "isNotNull"];
|
|
71
|
+
/**
|
|
72
|
+
* Upper bound for a restored page size, mirroring the surface descriptor's
|
|
73
|
+
* `limits.maxQueryRows`. A link is an untrusted input into a server query
|
|
74
|
+
* (#2452), so `?size=1000000` must clamp rather than become a row budget.
|
|
75
|
+
*/
|
|
76
|
+
export declare const CONTENT_LIST_MAX_PAGE_SIZE = 200;
|
|
77
|
+
/** Why one piece of a restored view was discarded. */
|
|
78
|
+
export type ContentListStateDropReason =
|
|
79
|
+
/** The column id is not published by the adapter at all. */
|
|
80
|
+
'unknown-column'
|
|
81
|
+
/** The column exists but is search-only and never published. */
|
|
82
|
+
| 'hidden-column'
|
|
83
|
+
/** The column is structural (`select`/`actions`) and carries no query. */
|
|
84
|
+
| 'structural-column'
|
|
85
|
+
/** The operator is outside `CONTENT_LIST_FILTER_OPERATORS`. */
|
|
86
|
+
| 'unsupported-operator'
|
|
87
|
+
/** The value is missing, blank, or not expressible as normalized text. */
|
|
88
|
+
| 'unsupported-value'
|
|
89
|
+
/** The entry is not shaped like the state it claims to be. */
|
|
90
|
+
| 'malformed'
|
|
91
|
+
/** The value was outside the accepted range and was clamped or reset. */
|
|
92
|
+
| 'out-of-range';
|
|
93
|
+
/** Which part of the view a drop applies to. */
|
|
94
|
+
export type ContentListStateDropScope =
|
|
95
|
+
/** The payload as a whole was not shaped like a view state. */
|
|
96
|
+
'state' | 'search' | 'filter' | 'sorting' | 'page' | 'pageSize' | 'columnOrder' | 'columnVisibility' | 'columnWidths' | 'columnPinning';
|
|
97
|
+
/**
|
|
98
|
+
* One discarded piece of a restored view. Reported rather than thrown so a UI
|
|
99
|
+
* can tell the operator "this saved view referenced a column that no longer
|
|
100
|
+
* exists" instead of failing to open the list.
|
|
101
|
+
*/
|
|
102
|
+
export interface ContentListStateDrop {
|
|
103
|
+
scope: ContentListStateDropScope;
|
|
104
|
+
reason: ContentListStateDropReason;
|
|
105
|
+
columnId?: string;
|
|
106
|
+
detail?: string;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* The validated subset of a view state.
|
|
110
|
+
*
|
|
111
|
+
* Deliberately `Partial`: only the aspects the source actually carried are
|
|
112
|
+
* present, and selection/expansion are never present at all.
|
|
113
|
+
*/
|
|
114
|
+
export interface ContentListViewStateSanitization {
|
|
115
|
+
state: Partial<DataTableViewState>;
|
|
116
|
+
dropped: ContentListStateDrop[];
|
|
117
|
+
}
|
|
118
|
+
export interface ContentListStateValidationOptions {
|
|
119
|
+
/** Overrides the clamp on a restored page size. */
|
|
120
|
+
maxPageSize?: number;
|
|
121
|
+
}
|
|
122
|
+
export interface ContentListUrlStateOptions extends ContentListStateValidationOptions {
|
|
123
|
+
/**
|
|
124
|
+
* Namespaces every owned parameter, so two lists can share one URL. With a
|
|
125
|
+
* prefix set, an unrecognized prefixed parameter is reported rather than
|
|
126
|
+
* ignored as a foreign parameter.
|
|
127
|
+
*/
|
|
128
|
+
prefix?: string;
|
|
129
|
+
/**
|
|
130
|
+
* The page size that is considered the default and therefore omitted from —
|
|
131
|
+
* and filled back in by — the query string. Hosts that seed a page size must
|
|
132
|
+
* pass the same value both ways or a clean link will restore as unpaginated.
|
|
133
|
+
*/
|
|
134
|
+
defaultPageSize?: number | null;
|
|
135
|
+
}
|
|
136
|
+
/** Reading a URL additionally reports what it refused to restore. */
|
|
137
|
+
export interface ContentListUrlStateReading extends ContentListViewStateSanitization {
|
|
138
|
+
state: Partial<DataTableViewState>;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* The single validator behind both restoration paths.
|
|
142
|
+
*
|
|
143
|
+
* Every restored aspect is re-derived from the adapter's published vocabulary,
|
|
144
|
+
* so neither a crafted query string nor a tampered saved view can introduce a
|
|
145
|
+
* filter, a sort rule, or a projection on a column the surface does not
|
|
146
|
+
* publish. Invalid input is dropped and reported, never thrown, and selection
|
|
147
|
+
* and expansion are never emitted.
|
|
148
|
+
*/
|
|
149
|
+
export declare function sanitizeContentListViewState(input: unknown, options?: ContentListStateValidationOptions): ContentListViewStateSanitization;
|
|
150
|
+
/**
|
|
151
|
+
* Merges a patch onto a controller's current state, validating it first.
|
|
152
|
+
*
|
|
153
|
+
* INVARIANT: no exported path may apply unvalidated state to a controller.
|
|
154
|
+
* This function is the only application point the package publishes, and it is
|
|
155
|
+
* routinely composed with values that came from somewhere untrusted — a query
|
|
156
|
+
* string, a `localStorage` blob, an agent command. Sanitizing here means the
|
|
157
|
+
* composition `applyContentListViewState(controller, storedView.snapshot.state)`
|
|
158
|
+
* is safe even though the store's read path deliberately keeps the raw payload
|
|
159
|
+
* so a stale view's drops can still be reported (see
|
|
160
|
+
* `restoreContentListSavedView`). Sanitization is idempotent, so calling this
|
|
161
|
+
* with an already-validated patch changes nothing.
|
|
162
|
+
*
|
|
163
|
+
* Only the *patch* is sanitized, never the merged result: the sanitizer never
|
|
164
|
+
* emits selection or expansion, so sanitizing the merge would silently clear
|
|
165
|
+
* the operator's current selection.
|
|
166
|
+
*
|
|
167
|
+
* Restoration is a state replacement rather than a command: it is not a user
|
|
168
|
+
* interaction and must not reset the page the way `setSearch`/`setFilters` do.
|
|
169
|
+
* Aspects the patch omits — selection and expansion above all — are carried
|
|
170
|
+
* over from the controller untouched.
|
|
171
|
+
*
|
|
172
|
+
* Use {@link sanitizeContentListViewState} directly when the caller needs to
|
|
173
|
+
* report what was refused.
|
|
174
|
+
*/
|
|
175
|
+
export declare function applyContentListViewState(controller: DataTableController, patch: Partial<DataTableViewState>, options?: ContentListStateValidationOptions): DataTableTransition;
|
|
176
|
+
/**
|
|
177
|
+
* Serializes a view state into shareable query parameters.
|
|
178
|
+
*
|
|
179
|
+
* The state is validated on the way out as well as on the way in: a link this
|
|
180
|
+
* module produces can never carry a filter or sort on a column the surface
|
|
181
|
+
* does not publish, even if the caller's controller somehow holds one.
|
|
182
|
+
* Defaults are omitted so an untouched list produces a clean URL.
|
|
183
|
+
*/
|
|
184
|
+
export declare function contentListViewStateToSearchParams(state: Partial<DataTableViewState>, options?: ContentListUrlStateOptions): URLSearchParams;
|
|
185
|
+
/**
|
|
186
|
+
* Writes the owned parameters into a copy of an existing query string,
|
|
187
|
+
* preserving every parameter this module does not own (routing keys, campaign
|
|
188
|
+
* tags, a sibling list's prefixed parameters).
|
|
189
|
+
*/
|
|
190
|
+
export declare function mergeContentListViewStateIntoSearchParams(params: URLSearchParams, state: Partial<DataTableViewState>, options?: ContentListUrlStateOptions): URLSearchParams;
|
|
191
|
+
/**
|
|
192
|
+
* Reads a view state from query parameters and reports everything it refused.
|
|
193
|
+
*
|
|
194
|
+
* Foreign parameters are ignored silently: a shared link routinely carries
|
|
195
|
+
* routing and campaign keys this module knows nothing about, and reporting
|
|
196
|
+
* them as drops would bury the reports that matter. A parameter that *looks*
|
|
197
|
+
* like a content-list filter — a known column, or any name carrying a known
|
|
198
|
+
* operator suffix — is reported when it is refused, which is what makes a
|
|
199
|
+
* crafted `description=` or `evil.contains=` visible rather than silent.
|
|
200
|
+
*/
|
|
201
|
+
export declare function readContentListViewStateFromSearchParams(params: URLSearchParams, options?: ContentListUrlStateOptions): ContentListUrlStateReading;
|
|
202
|
+
/**
|
|
203
|
+
* Reads a view state from query parameters.
|
|
204
|
+
*
|
|
205
|
+
* The result always carries `search`, `filters`, `sorting`, `page`, and
|
|
206
|
+
* `pageSize` — an absent parameter restores that aspect's default, so a clean
|
|
207
|
+
* link restores a clean view rather than leaving stale state in place. Use
|
|
208
|
+
* {@link readContentListViewStateFromSearchParams} when the refusals matter.
|
|
209
|
+
*/
|
|
210
|
+
export declare function contentListViewStateFromSearchParams(params: URLSearchParams, options?: ContentListUrlStateOptions): Partial<DataTableViewState>;
|
|
211
|
+
//# sourceMappingURL=content-list-url-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-list-url-state.d.ts","sourceRoot":"","sources":["../../src/svelte/content-list-url-state.ts"],"names":[],"mappings":"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"}
|