@happyvertical/smrt-content 0.44.0 → 0.44.1

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.
Files changed (44) hide show
  1. package/agents/content-list.md +61 -5
  2. package/dist/chunks/{content-corrections-BxfA3tW1.js → content-corrections-B4mPd_th.js} +6 -3
  3. package/dist/chunks/content-corrections-B4mPd_th.js.map +1 -0
  4. package/dist/chunks/{content-query-B4cHfrig.js → content-query-D1bLBzMT.js} +6 -6
  5. package/dist/chunks/{content-query-B4cHfrig.js.map → content-query-D1bLBzMT.js.map} +1 -1
  6. package/dist/chunks/{content-reviews-BWrYEQc6.js → content-reviews-lBoqJY7Y.js} +6 -3
  7. package/dist/chunks/content-reviews-lBoqJY7Y.js.map +1 -0
  8. package/dist/chunks/{content-versions-OEYQVrUf.js → content-versions-B0OUI1ut.js} +6 -3
  9. package/dist/chunks/content-versions-B0OUI1ut.js.map +1 -0
  10. package/dist/chunks/{contents-BrGxWH3s.js → contents-CRG2Snrb.js} +2 -2
  11. package/dist/chunks/{contents-BrGxWH3s.js.map → contents-CRG2Snrb.js.map} +1 -1
  12. package/dist/content-contribution.d.ts +1 -1
  13. package/dist/content-contribution.d.ts.map +1 -1
  14. package/dist/index.js +12 -9
  15. package/dist/index.js.map +1 -1
  16. package/dist/manifest.json +33 -6
  17. package/dist/server/content-list-actions.d.ts +10 -3
  18. package/dist/server/content-list-actions.d.ts.map +1 -1
  19. package/dist/server/content-list-lifecycle.d.ts +20 -0
  20. package/dist/server/content-list-lifecycle.d.ts.map +1 -0
  21. package/dist/server.d.ts +1 -0
  22. package/dist/server.d.ts.map +1 -1
  23. package/dist/server.js +189 -25
  24. package/dist/server.js.map +1 -1
  25. package/dist/smrt-knowledge.json +17 -5
  26. package/dist/svelte/components/ContentList.svelte +193 -22
  27. package/dist/svelte/components/ContentList.svelte.d.ts +5 -0
  28. package/dist/svelte/components/ContentList.svelte.d.ts.map +1 -1
  29. package/dist/svelte/components/ContentListLifecyclePanel.svelte +341 -0
  30. package/dist/svelte/components/ContentListLifecyclePanel.svelte.d.ts +17 -0
  31. package/dist/svelte/components/ContentListLifecyclePanel.svelte.d.ts.map +1 -0
  32. package/dist/svelte/content-list-lifecycle.d.ts +132 -0
  33. package/dist/svelte/content-list-lifecycle.d.ts.map +1 -0
  34. package/dist/svelte/content-list-lifecycle.js +439 -0
  35. package/dist/svelte/i18n.contribution.d.ts +27 -0
  36. package/dist/svelte/i18n.contribution.d.ts.map +1 -1
  37. package/dist/svelte/i18n.contribution.js +27 -0
  38. package/dist/svelte/index.d.ts +1 -0
  39. package/dist/svelte/index.d.ts.map +1 -1
  40. package/dist/svelte/index.js +1 -0
  41. package/package.json +15 -15
  42. package/dist/chunks/content-corrections-BxfA3tW1.js.map +0 -1
  43. package/dist/chunks/content-reviews-BWrYEQc6.js.map +0 -1
  44. package/dist/chunks/content-versions-OEYQVrUf.js.map +0 -1
@@ -0,0 +1,341 @@
1
+ <script lang="ts">
2
+ import type {
3
+ DataSurfaceActionResult,
4
+ DataSurfaceRowId,
5
+ } from '@happyvertical/smrt-ui/data';
6
+ import { Modal } from '@happyvertical/smrt-ui/feedback';
7
+ import { Input, Select } from '@happyvertical/smrt-ui/forms';
8
+ import { useI18n } from '@happyvertical/smrt-ui/i18n';
9
+ import { Button } from '@happyvertical/smrt-ui/ui';
10
+ import { onDestroy, untrack } from 'svelte';
11
+ import {
12
+ type ContentListLifecycleActionId,
13
+ type ContentListLifecycleBinding,
14
+ type ContentListLifecycleSnapshot,
15
+ type ContentListRestoreStatus,
16
+ createContentListLifecycleController,
17
+ } from '../content-list-lifecycle.js';
18
+ import type { ContentListDataQueryRequest } from '../content-list-query.js';
19
+ import { M } from '../i18n.contribution.js';
20
+
21
+ const { t } = useI18n();
22
+
23
+ interface Props {
24
+ binding: ContentListLifecycleBinding;
25
+ mode: 'active' | 'trash';
26
+ selectedRowIds: DataSurfaceRowId[];
27
+ query?: ContentListDataQueryRequest;
28
+ queryFingerprint?: string;
29
+ exactMatchingCount?: number;
30
+ viewKey: string;
31
+ oncomplete: (result: DataSurfaceActionResult) => void;
32
+ }
33
+
34
+ let {
35
+ binding,
36
+ mode,
37
+ selectedRowIds,
38
+ query = undefined,
39
+ queryFingerprint = undefined,
40
+ exactMatchingCount = undefined,
41
+ viewKey,
42
+ oncomplete,
43
+ }: Props = $props();
44
+
45
+ const controller = createContentListLifecycleController(untrack(() => binding));
46
+ let snapshot = $state<ContentListLifecycleSnapshot>(controller.snapshot());
47
+ let lastResult = $state<ContentListLifecycleSnapshot['summary']>();
48
+ let dialogOpen = $state(false);
49
+ let countConfirmation = $state('');
50
+ let restoreStatus = $state<ContentListRestoreStatus>('draft');
51
+ let intent = $state<'selected' | 'all-matching'>('selected');
52
+
53
+ const unsubscribe = controller.subscribe((next) => {
54
+ snapshot = next;
55
+ });
56
+ onDestroy(unsubscribe);
57
+
58
+ $effect(() => {
59
+ controller.invalidate(viewKey);
60
+ if (snapshot.status === 'idle') {
61
+ dialogOpen = false;
62
+ countConfirmation = '';
63
+ }
64
+ });
65
+
66
+ const busy = $derived(
67
+ snapshot.status === 'previewing' ||
68
+ snapshot.status === 'applying' ||
69
+ snapshot.replayRequired === true,
70
+ );
71
+ const selectedCount = $derived(selectedRowIds.length);
72
+ const selectionLimit = $derived(binding.maxSelectionSize ?? 200);
73
+ const matchingSelectionTooLarge = $derived(
74
+ exactMatchingCount !== undefined && exactMatchingCount > selectionLimit,
75
+ );
76
+ const resolvedCount = $derived(snapshot.summary?.resolvedCount ?? 0);
77
+ const requiresTypedCount = $derived(snapshot.actionId === 'permanent-delete');
78
+ const confirmationValid = $derived(
79
+ !requiresTypedCount || countConfirmation.trim() === String(resolvedCount),
80
+ );
81
+
82
+ function selectionFor(scope: 'selected' | 'all-matching') {
83
+ return scope === 'selected'
84
+ ? { scope: 'explicit-ids' as const, rowIds: [...selectedRowIds] }
85
+ : {
86
+ scope: 'all-matching' as const,
87
+ queryFingerprint: queryFingerprint ?? '',
88
+ };
89
+ }
90
+
91
+ async function begin(
92
+ actionId: ContentListLifecycleActionId,
93
+ scope: 'selected' | 'all-matching' = 'selected',
94
+ ) {
95
+ intent = scope;
96
+ countConfirmation = '';
97
+ dialogOpen = true;
98
+ await controller.preview({
99
+ actionId,
100
+ selection: selectionFor(scope),
101
+ expectedCount:
102
+ scope === 'selected' ? selectedCount : (exactMatchingCount ?? -1),
103
+ ...(scope === 'all-matching' && query ? { query } : {}),
104
+ ...(actionId === 'restore' ? { restoreStatus } : {}),
105
+ viewKey,
106
+ });
107
+ }
108
+
109
+ async function apply() {
110
+ const next = await controller.apply(resolvedCount);
111
+ if (next.status !== 'succeeded' || !next.result) return;
112
+ lastResult = next.summary;
113
+ oncomplete(next.result);
114
+ }
115
+
116
+ function close() {
117
+ if (busy) return;
118
+ dialogOpen = false;
119
+ controller.reset();
120
+ }
121
+
122
+ function retryPreview() {
123
+ if (!snapshot.actionId) return;
124
+ void begin(snapshot.actionId, intent);
125
+ }
126
+ </script>
127
+
128
+ <section
129
+ class="lifecycle"
130
+ aria-label={mode === 'trash'
131
+ ? t(M['content.content_list.trash_actions'])
132
+ : t(M['content.content_list.lifecycle_actions'])}
133
+ >
134
+ {#if mode === 'active'}
135
+ <Button
136
+ variant="ghost"
137
+ size="sm"
138
+ type="button"
139
+ disabled={selectedCount === 0 || busy}
140
+ onclick={() => begin('move-to-trash')}
141
+ >{t(M['content.content_list.move_selected_to_trash'])}</Button>
142
+ {:else}
143
+ <Select
144
+ aria-label={t(M['content.content_list.restore_destination'])}
145
+ bind:value={restoreStatus}
146
+ disabled={busy || dialogOpen}
147
+ >
148
+ <option value="draft">{t(M['content.content_list.restore_as_draft'])}</option>
149
+ <option value="review">{t(M['content.content_list.restore_for_review'])}</option>
150
+ <option value="published">{t(M['content.content_list.restore_and_publish'])}</option>
151
+ </Select>
152
+ <Button
153
+ variant="ghost"
154
+ size="sm"
155
+ type="button"
156
+ disabled={selectedCount === 0 || busy}
157
+ onclick={() => begin('restore')}
158
+ >{t(M['content.content_list.restore_selected'])}</Button>
159
+ <Button
160
+ variant="ghost"
161
+ size="sm"
162
+ type="button"
163
+ disabled={selectedCount === 0 || busy}
164
+ onclick={() => begin('permanent-delete')}
165
+ >{t(M['content.content_list.delete_selected_permanently'])}</Button>
166
+ <Button
167
+ variant="ghost"
168
+ size="sm"
169
+ type="button"
170
+ disabled={exactMatchingCount === undefined || exactMatchingCount === 0 || matchingSelectionTooLarge || !query || !queryFingerprint || busy}
171
+ onclick={() => begin('permanent-delete', 'all-matching')}
172
+ >{t(M['content.content_list.empty_trash'])}</Button>
173
+ {/if}
174
+ </section>
175
+ {#if mode === 'trash' && matchingSelectionTooLarge}
176
+ <p class="selection-limit" role="status">
177
+ {t(M['content.content_list.empty_trash_limit'], { count: selectionLimit })}
178
+ </p>
179
+ {/if}
180
+ {#if lastResult}
181
+ <div class="lifecycle-result" aria-live="polite">
182
+ <p>
183
+ {t(M['content.content_list.lifecycle_completed_summary'], {
184
+ accepted: lastResult.accepted,
185
+ skipped: lastResult.skipped,
186
+ failed: lastResult.failed,
187
+ reference: lastResult.auditReference,
188
+ })}
189
+ </p>
190
+ {#if lastResult.skipped > 0 || lastResult.failed > 0}
191
+ <ul aria-label={t(M['content.content_list.lifecycle_exceptions'])}>
192
+ {#each lastResult.outcomes.filter((outcome) => outcome.status !== 'accepted') as outcome}
193
+ <li>{String(outcome.rowId)}: {outcome.reason ?? outcome.status}</li>
194
+ {/each}
195
+ </ul>
196
+ {/if}
197
+ </div>
198
+ {/if}
199
+
200
+ <Modal
201
+ open={dialogOpen}
202
+ title={snapshot.actionId === 'permanent-delete'
203
+ ? t(M['content.content_list.permanent_delete_title'])
204
+ : snapshot.actionId === 'restore'
205
+ ? t(M['content.content_list.restore_title'])
206
+ : t(M['content.content_list.move_to_trash_title'])}
207
+ size="md"
208
+ closeOnBackdrop={!busy}
209
+ closeOnEscape={!busy}
210
+ showClose={!busy}
211
+ onClose={close}
212
+ ariaDescribedBy="content-lifecycle-summary"
213
+ >
214
+ <div class="preview" aria-live="polite" aria-busy={busy}>
215
+ {#if snapshot.status === 'previewing'}
216
+ <p id="content-lifecycle-summary">
217
+ {t(M['content.content_list.lifecycle_resolving'])}
218
+ </p>
219
+ {:else if snapshot.status === 'applying'}
220
+ <p role="status">
221
+ {t(M['content.content_list.workflow_applying'])}
222
+ </p>
223
+ {#if snapshot.summary}
224
+ <p id="content-lifecycle-summary">
225
+ {t(M['content.content_list.lifecycle_resolved'], {
226
+ count: snapshot.summary.resolvedCount,
227
+ })}
228
+ </p>
229
+ {/if}
230
+ {:else if snapshot.summary}
231
+ <p id="content-lifecycle-summary">
232
+ {t(M['content.content_list.lifecycle_resolved'], {
233
+ count: snapshot.summary.resolvedCount,
234
+ })}
235
+ </p>
236
+ {#if snapshot.summary.representativeLabels.length > 0}
237
+ <p>{t(M['content.content_list.lifecycle_examples'], {
238
+ examples: snapshot.summary.representativeLabels.join(', '),
239
+ })}</p>
240
+ {/if}
241
+ <p>
242
+ {t(M['content.content_list.lifecycle_eligibility'], {
243
+ accepted: snapshot.summary.accepted,
244
+ skipped: snapshot.summary.skipped,
245
+ failed: snapshot.summary.failed,
246
+ })}
247
+ </p>
248
+ {#if snapshot.actionId === 'permanent-delete'}
249
+ <p>{t(M['content.content_list.permanent_delete_warning'], {
250
+ count: snapshot.summary.resolvedCount,
251
+ })}</p>
252
+ <Input
253
+ aria-label={t(M['content.content_list.permanent_delete_count_label'])}
254
+ inputmode="numeric"
255
+ autocomplete="off"
256
+ bind:value={countConfirmation}
257
+ disabled={busy || snapshot.status !== 'ready'}
258
+ />
259
+ {/if}
260
+ {/if}
261
+
262
+ {#if snapshot.error}
263
+ <p class="error" role="alert">{snapshot.error}</p>
264
+ {/if}
265
+ {#if snapshot.status === 'succeeded' && snapshot.summary}
266
+ <p role="status">
267
+ {t(M['content.content_list.lifecycle_completed_audit'], {
268
+ reference: snapshot.summary.auditReference,
269
+ })}
270
+ </p>
271
+ {#if snapshot.summary.skipped > 0 || snapshot.summary.failed > 0}
272
+ <ul aria-label={t(M['content.content_list.lifecycle_exceptions'])}>
273
+ {#each snapshot.summary.outcomes.filter((outcome) => outcome.status !== 'accepted') as outcome}
274
+ <li>{String(outcome.rowId)}: {outcome.reason ?? outcome.status}</li>
275
+ {/each}
276
+ </ul>
277
+ {/if}
278
+ {/if}
279
+
280
+ <div class="actions">
281
+ <Button variant="ghost" type="button" disabled={busy} onclick={close}>
282
+ {snapshot.status === 'succeeded'
283
+ ? t(M['content.content_list.lifecycle_close'])
284
+ : t(M['content.content_list.cancel'])}
285
+ </Button>
286
+ {#if snapshot.status === 'failed' && snapshot.renewalRequired}
287
+ <Button type="button" onclick={retryPreview}>
288
+ {t(M['content.content_list.lifecycle_renew_preview'])}
289
+ </Button>
290
+ {:else if snapshot.status === 'failed'}
291
+ <Button type="button" onclick={apply}>
292
+ {t(M['content.content_list.retry'])}
293
+ </Button>
294
+ {:else if snapshot.status === 'ready'}
295
+ <Button
296
+ type="button"
297
+ disabled={!confirmationValid || resolvedCount === 0}
298
+ onclick={apply}
299
+ >{t(M['content.content_list.lifecycle_confirm_count'], {
300
+ count: resolvedCount,
301
+ })}</Button>
302
+ {/if}
303
+ </div>
304
+ </div>
305
+ </Modal>
306
+
307
+ <style>
308
+ .lifecycle,
309
+ .actions {
310
+ display: flex;
311
+ align-items: center;
312
+ flex-wrap: wrap;
313
+ gap: 0.5rem;
314
+ }
315
+
316
+ .preview {
317
+ display: grid;
318
+ gap: 0.75rem;
319
+ }
320
+
321
+ .preview p {
322
+ margin: 0;
323
+ }
324
+
325
+ .lifecycle-result p {
326
+ margin: 0.5rem 0 0;
327
+ }
328
+
329
+ .selection-limit {
330
+ margin: 0.5rem 0 0;
331
+ }
332
+
333
+ .actions {
334
+ justify-content: flex-end;
335
+ margin-top: 0.5rem;
336
+ }
337
+
338
+ .error {
339
+ color: var(--smrt-color-error, #b3261e);
340
+ }
341
+ </style>
@@ -0,0 +1,17 @@
1
+ import type { DataSurfaceActionResult, DataSurfaceRowId } from '@happyvertical/smrt-ui/data';
2
+ import { type ContentListLifecycleBinding } from '../content-list-lifecycle.js';
3
+ import type { ContentListDataQueryRequest } from '../content-list-query.js';
4
+ interface Props {
5
+ binding: ContentListLifecycleBinding;
6
+ mode: 'active' | 'trash';
7
+ selectedRowIds: DataSurfaceRowId[];
8
+ query?: ContentListDataQueryRequest;
9
+ queryFingerprint?: string;
10
+ exactMatchingCount?: number;
11
+ viewKey: string;
12
+ oncomplete: (result: DataSurfaceActionResult) => void;
13
+ }
14
+ declare const ContentListLifecyclePanel: import("svelte").Component<Props, {}, "">;
15
+ type ContentListLifecyclePanel = ReturnType<typeof ContentListLifecyclePanel>;
16
+ export default ContentListLifecyclePanel;
17
+ //# sourceMappingURL=ContentListLifecyclePanel.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ContentListLifecyclePanel.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/ContentListLifecyclePanel.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,uBAAuB,EACvB,gBAAgB,EACjB,MAAM,6BAA6B,CAAC;AAMrC,OAAO,EAEL,KAAK,2BAA2B,EAIjC,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAI5E,UAAU,KAAK;IACb,OAAO,EAAE,2BAA2B,CAAC;IACrC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;IACzB,cAAc,EAAE,gBAAgB,EAAE,CAAC;IACnC,KAAK,CAAC,EAAE,2BAA2B,CAAC;IACpC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,CAAC,MAAM,EAAE,uBAAuB,KAAK,IAAI,CAAC;CACvD;AAkPD,QAAA,MAAM,yBAAyB,2CAAwC,CAAC;AACxE,KAAK,yBAAyB,GAAG,UAAU,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAC9E,eAAe,yBAAyB,CAAC"}
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Browser-safe trash, restore, and permanent-delete orchestration for
3
+ * ContentList (#2454).
4
+ *
5
+ * Lifecycle authority stays on the server. The browser supplies a selection
6
+ * hint and expected count, then renders the server's resolved preview. Apply
7
+ * must reuse that preview's opaque confirmation token and authority-bound
8
+ * selection while using a fresh request id for each transport attempt; a
9
+ * changed query, count, permission, row revision, or eligibility is therefore
10
+ * rejected by the principal-bound server adapter rather than being guessed
11
+ * from stale list rows.
12
+ */
13
+ import type { DataSurfaceActionResult, DataSurfaceIdentity, DataSurfaceJsonValue, DataSurfaceSelectionReference } from '@happyvertical/smrt-ui/data';
14
+ import type { ContentListDataQueryRequest } from './content-list-query.js';
15
+ export declare const CONTENT_LIST_LIFECYCLE_ACTION_IDS: readonly ["move-to-trash", "restore", "permanent-delete"];
16
+ export type ContentListLifecycleActionId = (typeof CONTENT_LIST_LIFECYCLE_ACTION_IDS)[number];
17
+ export type ContentListRestoreStatus = 'draft' | 'review' | 'published';
18
+ export interface ContentListLifecycleTarget {
19
+ /** Canonical server query. Required for all-matching selections. */
20
+ query?: ContentListDataQueryRequest;
21
+ /** Count visible when the operator requested the preview. */
22
+ expectedCount: number;
23
+ /** Operator-entered server preview count, sent only on permanent-delete apply. */
24
+ confirmedCount?: number;
25
+ }
26
+ export interface ContentListLifecycleRequest {
27
+ version: 1;
28
+ requestId: string;
29
+ identity: DataSurfaceIdentity;
30
+ actionId: ContentListLifecycleActionId;
31
+ phase: 'preview' | 'apply';
32
+ selection: DataSurfaceSelectionReference;
33
+ payload?: DataSurfaceJsonValue;
34
+ expectedRevision: number;
35
+ target: ContentListLifecycleTarget;
36
+ confirmationToken?: string;
37
+ idempotencyKey?: string;
38
+ }
39
+ export interface ContentListLifecycleClient {
40
+ preview(request: ContentListLifecycleRequest): Promise<DataSurfaceActionResult>;
41
+ apply(request: ContentListLifecycleRequest): Promise<DataSurfaceActionResult>;
42
+ }
43
+ export interface ContentListLifecycleBinding {
44
+ client: ContentListLifecycleClient;
45
+ /** Server-owned surface revision. Never derived from list rows. */
46
+ revision?: number;
47
+ /** Override the default ContentList identity. */
48
+ identity?: DataSurfaceIdentity;
49
+ /** Server action-selection cap. Defaults to 200 and fails closed in the UI. */
50
+ maxSelectionSize?: number;
51
+ }
52
+ export interface ContentListLifecycleTransportOptions {
53
+ apiBaseUrl?: string;
54
+ path?: string;
55
+ fetch?: typeof globalThis.fetch;
56
+ headers?: HeadersInit | (() => HeadersInit | Promise<HeadersInit>);
57
+ credentials?: RequestCredentials;
58
+ }
59
+ export type ContentListLifecycleTransportErrorReason = 'network_failure' | 'invalid_json' | 'http_error' | 'invalid_result';
60
+ export declare class ContentListLifecycleTransportError extends Error {
61
+ readonly status?: number;
62
+ readonly reason: ContentListLifecycleTransportErrorReason;
63
+ constructor(message: string, status?: number, reason?: ContentListLifecycleTransportErrorReason);
64
+ }
65
+ export interface ContentListLifecycleOutcome {
66
+ rowId: string | number;
67
+ status: 'accepted' | 'skipped' | 'failed';
68
+ reason?: string;
69
+ /** Canonical runtime subtype, for example a Document or Mirror. */
70
+ resourceType?: string;
71
+ /** Canonical resource id used by the server mutation. */
72
+ resourceId?: string;
73
+ }
74
+ export interface ContentListLifecycleSummary {
75
+ resolvedCount: number;
76
+ accepted: number;
77
+ skipped: number;
78
+ failed: number;
79
+ outcomes: ContentListLifecycleOutcome[];
80
+ representativeLabels: string[];
81
+ expiresAt?: number;
82
+ /** Correlates the visible result with principal audit metadata. */
83
+ auditReference: string;
84
+ }
85
+ export type ContentListLifecycleControllerStatus = 'idle' | 'previewing' | 'ready' | 'applying' | 'succeeded' | 'failed';
86
+ export interface ContentListLifecycleSnapshot {
87
+ status: ContentListLifecycleControllerStatus;
88
+ actionId?: ContentListLifecycleActionId;
89
+ selection?: DataSurfaceSelectionReference;
90
+ summary?: ContentListLifecycleSummary;
91
+ result?: DataSurfaceActionResult;
92
+ error?: string;
93
+ /** True when the only safe recovery is another server preview. */
94
+ renewalRequired: boolean;
95
+ /** True while an ambiguous apply must replay its exact authority envelope. */
96
+ replayRequired?: boolean;
97
+ }
98
+ export interface ContentListLifecyclePreviewInput {
99
+ actionId: ContentListLifecycleActionId;
100
+ selection: DataSurfaceSelectionReference;
101
+ expectedCount: number;
102
+ query?: ContentListDataQueryRequest;
103
+ restoreStatus?: ContentListRestoreStatus;
104
+ /** Invalidates the preview when the visible query/selection changes. */
105
+ viewKey: string;
106
+ }
107
+ export interface ContentListLifecycleControllerOptions extends ContentListLifecycleBinding {
108
+ createRequestId?: () => string;
109
+ createIdempotencyKey?: () => string;
110
+ }
111
+ export interface ContentListLifecycleController {
112
+ snapshot(): ContentListLifecycleSnapshot;
113
+ subscribe(listener: (snapshot: ContentListLifecycleSnapshot) => void): () => void;
114
+ preview(input: ContentListLifecyclePreviewInput): Promise<ContentListLifecycleSnapshot>;
115
+ /**
116
+ * Apply only after the operator confirms the server-resolved count. The
117
+ * number is intentionally passed again rather than inferred by the UI.
118
+ */
119
+ apply(confirmedCount: number): Promise<ContentListLifecycleSnapshot>;
120
+ /** Drop a preview after any visible query or selection change. */
121
+ invalidate(viewKey: string): void;
122
+ reset(): void;
123
+ }
124
+ /** Authenticated fetch transport for the host's lifecycle route. */
125
+ export declare function createContentListLifecycleTransport(options?: ContentListLifecycleTransportOptions): ContentListLifecycleClient;
126
+ /** Parse the bounded, optional preview/apply details without trusting casts. */
127
+ export declare function readContentListLifecycleSummary(result: DataSurfaceActionResult): ContentListLifecycleSummary;
128
+ /** Framework-free state machine shared by UI and agent-driven lifecycle flows. */
129
+ export declare function createContentListLifecycleController(options: ContentListLifecycleControllerOptions): ContentListLifecycleController;
130
+ /** Keep unsuccessful rows selected so partial failures are visible/retryable. */
131
+ export declare function reconcileContentListLifecycleSelection(selectedRowIds: readonly (string | number)[], result: DataSurfaceActionResult): Array<string | number>;
132
+ //# sourceMappingURL=content-list-lifecycle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-list-lifecycle.d.ts","sourceRoot":"","sources":["../../src/svelte/content-list-lifecycle.ts"],"names":[],"mappings":"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"}