@happyvertical/smrt-content 0.44.0 → 0.45.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.
Files changed (52) hide show
  1. package/agents/content-list.md +93 -14
  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-BV41W2Dq.js} +6 -6
  5. package/dist/chunks/{content-query-B4cHfrig.js.map → content-query-BV41W2Dq.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-dZSBU8UF.js} +6 -3
  9. package/dist/chunks/content-versions-dZSBU8UF.js.map +1 -0
  10. package/dist/chunks/{contents-BrGxWH3s.js → contents-Ba7JHu2K.js} +2 -2
  11. package/dist/chunks/{contents-BrGxWH3s.js.map → contents-Ba7JHu2K.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 +2268 -4345
  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-data-surface.d.ts +29 -0
  20. package/dist/server/content-list-data-surface.d.ts.map +1 -0
  21. package/dist/server/content-list-lifecycle.d.ts +20 -0
  22. package/dist/server/content-list-lifecycle.d.ts.map +1 -0
  23. package/dist/server.d.ts +2 -0
  24. package/dist/server.d.ts.map +1 -1
  25. package/dist/server.js +247 -25
  26. package/dist/server.js.map +1 -1
  27. package/dist/smrt-knowledge.json +4291 -2510
  28. package/dist/svelte/components/ContentList.svelte +538 -40
  29. package/dist/svelte/components/ContentList.svelte.d.ts +6 -1
  30. package/dist/svelte/components/ContentList.svelte.d.ts.map +1 -1
  31. package/dist/svelte/components/ContentListLifecyclePanel.svelte +387 -0
  32. package/dist/svelte/components/ContentListLifecyclePanel.svelte.d.ts +17 -0
  33. package/dist/svelte/components/ContentListLifecyclePanel.svelte.d.ts.map +1 -0
  34. package/dist/svelte/content-list-controller.d.ts +10 -0
  35. package/dist/svelte/content-list-controller.d.ts.map +1 -1
  36. package/dist/svelte/content-list-controller.js +111 -26
  37. package/dist/svelte/content-list-data-surface.d.ts +45 -0
  38. package/dist/svelte/content-list-data-surface.d.ts.map +1 -0
  39. package/dist/svelte/content-list-data-surface.js +196 -0
  40. package/dist/svelte/content-list-lifecycle.d.ts +132 -0
  41. package/dist/svelte/content-list-lifecycle.d.ts.map +1 -0
  42. package/dist/svelte/content-list-lifecycle.js +439 -0
  43. package/dist/svelte/i18n.contribution.d.ts +27 -0
  44. package/dist/svelte/i18n.contribution.d.ts.map +1 -1
  45. package/dist/svelte/i18n.contribution.js +27 -0
  46. package/dist/svelte/index.d.ts +2 -0
  47. package/dist/svelte/index.d.ts.map +1 -1
  48. package/dist/svelte/index.js +2 -0
  49. package/package.json +15 -15
  50. package/dist/chunks/content-corrections-BxfA3tW1.js.map +0 -1
  51. package/dist/chunks/content-reviews-BWrYEQc6.js.map +0 -1
  52. package/dist/chunks/content-versions-OEYQVrUf.js.map +0 -1
@@ -1,12 +1,67 @@
1
1
  <!-- Module doc for packages/content/AGENTS.md. Linked from the Modules table there. -->
2
2
 
3
- # ContentList: shared adapter, server query, URL state, saved views
3
+ # ContentList: shared adapter, query state, saved views, and lifecycle
4
4
 
5
5
  `ContentList` reads every row, column, filter, and action through one shared
6
6
  adapter (`src/svelte/content-list-controller.ts`) over a `DataTableController`,
7
7
  and can source those rows either from a client array or from the bounded
8
8
  content query endpoint. This doc covers both modes end to end.
9
9
 
10
+ ## Trash, restore, and permanent deletion (#2454)
11
+
12
+ Lifecycle controls are an opt-in server-authoritative seam. Pass a
13
+ `ContentListLifecycleBinding` as `lifecycle`; set `lifecycleMode="trash"` for a
14
+ trash view. Trash mode locks the live status predicate to `deleted`, hides the
15
+ ordinary status control, removes legacy edit/delete buttons, and exposes
16
+ restore, permanent-delete, and empty-trash actions. Omit the binding and
17
+ ContentList keeps its existing `onDelete` behavior.
18
+
19
+ The browser never treats rendered rows as mutation authority. It sends a
20
+ selection reference, canonical query for `all-matching`, expected count, and
21
+ server revision through `createContentListLifecycleTransport()`. Preview,
22
+ initial apply, and every replay use distinct per-attempt request ids. Apply and
23
+ replay retain the opaque confirmation and idempotency authority envelope, while
24
+ the result's `auditReference` continues to identify the original execution.
25
+ Changing the query or selection invalidates the preview; expired, stale-revision,
26
+ row-revision, query-fingerprint, token, or matching-count failures require a
27
+ new preview.
28
+
29
+ Mount `createContentListLifecycleRoute()` from
30
+ `@happyvertical/smrt-content/server` at the transport's default
31
+ `POST /api/v1/contents/lifecycle` path. The host supplies a callback that
32
+ authenticates each native `Request` and returns its current principal/tenant
33
+ action context. The route accepts only the three lifecycle action ids and
34
+ delegates to the same `createContentListActionAdapter()` instance used by agent
35
+ and bulk-workflow callers; it never owns a second authorization or mutation
36
+ path. Invalid JSON, missing authentication, and internal failures return
37
+ bounded error envelopes without exposing private error text.
38
+
39
+ Lifecycle action ids and restore input are stable:
40
+
41
+ | action | eligible server state | input / consequence |
42
+ |---|---|---|
43
+ | `move-to-trash` | not already `deleted` | soft-deletes content into trash |
44
+ | `restore` | `deleted` | `{ status: 'draft' \| 'review' \| 'published' }`; the server re-runs permission and publish-readiness checks |
45
+ | `permanent-delete` | `deleted` | irreversible deletion; the operator must type the exact server-resolved count |
46
+
47
+ Permanent deletion binds the row's `updated_at` revision into the final delete.
48
+ On PostgreSQL that comparison is part of the `DELETE` predicate and any cascade
49
+ shares its transaction; embedded adapters compare under the framework's shared
50
+ write queue before cascading. A writer racing preview/apply therefore produces
51
+ `row_revision_drifted` instead of deleting newer content.
52
+
53
+ The result details expose `count`, accepted/skipped/failed counts,
54
+ representative labels, canonical subtype-aware outcomes
55
+ (`resourceType`/`resourceId`), and `auditReference`. Applying a partial result
56
+ removes only accepted ids from selection, keeping skipped and failed rows
57
+ selected for inspection or retry. The exact query refreshes after success;
58
+ progress, partial exceptions, and the audit reference remain visible in a
59
+ polite live region. Empty trash is available only when an exact server count
60
+ and canonical query are present, so a page-sized guess cannot stand in for its
61
+ scope. `maxSelectionSize` defaults to 200; larger matching sets are announced
62
+ and fail closed until the operator narrows the query, matching the server's
63
+ bounded one-preview selection contract.
64
+
10
65
  ## Realtime, freshness, and background workflows (#2455)
11
66
 
12
67
  A server-backed list consumes the complete reactive seam exposed by
@@ -142,6 +197,25 @@ Adapter exports (also re-exported from `./svelte`): `createContentListController
142
197
  `resolveSelectedContentListRows`, `resolveSelectedContents`, plus the
143
198
  `CONTENT_LIST_*` identity constants.
144
199
 
200
+ ## Agent data-surface conformance (#2456)
201
+
202
+ `dataSurface` publishes the mounted list's readable columns, supported query
203
+ operators, visible state, selection scope, freshness, and only the actions its
204
+ mounted workflow/lifecycle bindings can execute. Visible commands run through
205
+ the registry's revision check; their acknowledgements describe the accepted
206
+ controller revision, while a disconnected registry refuses them.
207
+
208
+ For silent agent reads, register
209
+ `createContentListDataSurfaceDefinition({ collection, scope })` from
210
+ `@happyvertical/smrt-content/server` in the server-owned
211
+ `createDataSurfaceTools()` catalog. The factory resolves both the collection
212
+ and optional application scope from the live principal context, then delegates
213
+ rows, counts, facets, and offset continuations to `executeContentQuery()`.
214
+ Tenant, hidden, and sensitive fields remain outside discovery and projection;
215
+ the model request can only narrow the trusted scope. Durable actions continue
216
+ through `createContentListActionAdapter()` and its principal-bound
217
+ preview/apply contract rather than through the browser registry.
218
+
145
219
  Notes:
146
220
 
147
221
  - Controller modes are all `manual`: the adapter owns search, filters, sorting,
@@ -194,15 +268,19 @@ Notes:
194
268
  — which owns both in compact mode — is not mounted there. A page size arriving
195
269
  from a saved view or a surface command would otherwise strand the operator on
196
270
  page one, and a refresh over retained rows would be silent.
197
- - `dataSurface` registers the compact table only. Agent addressability for the
198
- grid and detailed presentations lands with #2456.
199
- - Compact mode stays mounted for empty and loading results — DataTable renders
200
- its own `empty` snippet and loading row — because it owns the mounted surface:
201
- swapping it for the shared empty panel unregisters the surface, and an agent
202
- whose own search returned nothing then gets `not_found` on the command that
203
- would undo it. The shared loading/empty panels are the card presentations'
204
- only; the `error` branch still replaces the list in every mode, since a load
205
- failure is host-driven rather than surface-driven.
271
+ - `dataSurface` registers at the `ContentList` boundary, independently of its
272
+ renderer. Grid, detailed, compact, loading, empty, and error presentations
273
+ therefore retain one stable surface identity. A `set-view` command updates
274
+ the published `viewMode` and revision before the renderer changes, so its
275
+ acknowledgement always describes the accepted presentation.
276
+ - The mounted snapshot publishes the controller state and normalized selection
277
+ together with the active query fingerprint, total, presentation, and
278
+ freshness (`stale`, `refreshing`, `offline`, last update, truncation, and
279
+ warnings). Context-only changes advance the same revision stream as visible
280
+ controller commands; stale command revisions are refused by the registry.
281
+ - Discovery publishes per-column search/filter/sort operators. A server-backed
282
+ list narrows the derived `site` column to read-only because the content query
283
+ translator cannot soundly project, filter, or sort that display value.
206
284
 
207
285
  ## ContentList server-backed mode (#2452)
208
286
 
@@ -965,10 +1043,11 @@ keeps the control from misstating the query; `readContentListFilter` stays
965
1043
  value-only and must not drive a control. Choosing any real option replaces every
966
1044
  filter on that column, so the operator is never stuck.
967
1045
 
968
- `review` is now offered outright. `deleted` is deliberately not: that is the
969
- trash lifecycle (#2454), and offering it here would imply a restore/purge
970
- affordance this list does not have. `Content.type` is freeform, so its option
971
- list is a display vocabulary rather than the model's domain.
1046
+ `review` is now offered outright. `deleted` is deliberately not part of the
1047
+ ordinary status vocabulary: the opt-in trash lifecycle locks that predicate
1048
+ and supplies the restore/purge affordances as one inseparable mode.
1049
+ `Content.type` is freeform, so its option list is a display vocabulary rather
1050
+ than the model's domain.
972
1051
 
973
1052
  Everything a restore or a translation refused is reported in one dismissible
974
1053
  notice rather than thrown — a stale link or an out-of-date saved view must still
@@ -52,8 +52,11 @@ var ContentCorrection = class extends SmrtObject {
52
52
  }
53
53
  }
54
54
  };
55
- __decorateClass([foreignKey("Content", { required: true })], ContentCorrection.prototype, "contentId", 2);
56
- __decorateClass([foreignKey("ContentVersion")], ContentCorrection.prototype, "contentVersionId", 2);
55
+ __decorateClass([foreignKey("Content", {
56
+ required: true,
57
+ onDelete: "CASCADE"
58
+ })], ContentCorrection.prototype, "contentId", 2);
59
+ __decorateClass([foreignKey("ContentVersion", { onDelete: "CASCADE" })], ContentCorrection.prototype, "contentVersionId", 2);
57
60
  __decorateClass([crossPackageRef("@happyvertical/smrt-facts:Fact")], ContentCorrection.prototype, "factId", 2);
58
61
  __decorateClass([crossPackageRef("@happyvertical/smrt-facts:Fact")], ContentCorrection.prototype, "replacementFactId", 2);
59
62
  __decorateClass([tenantId({ nullable: true })], ContentCorrection.prototype, "tenantId", 2);
@@ -104,4 +107,4 @@ var ContentCorrectionCollection = class extends SmrtCollection {
104
107
  //#endregion
105
108
  export { content_corrections_exports as n, ContentCorrection as r, ContentCorrectionCollection as t };
106
109
 
107
- //# sourceMappingURL=content-corrections-BxfA3tW1.js.map
110
+ //# sourceMappingURL=content-corrections-B4mPd_th.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-corrections-B4mPd_th.js","names":[],"sources":["../../src/content-correction.ts","../../src/content-corrections.ts"],"sourcesContent":["import type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport {\n crossPackageRef,\n foreignKey,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type {\n ContentCorrectionStatus,\n ContentCorrectionType,\n} from './content-governance';\n\nexport interface ContentCorrectionOptions extends SmrtObjectOptions {\n contentId?: string;\n contentVersionId?: string;\n factId?: string;\n replacementFactId?: string;\n correctionType?: ContentCorrectionType;\n status?: ContentCorrectionStatus;\n summary?: string;\n incorrectText?: string;\n correctedText?: string;\n publicNote?: string;\n metadata?: string | Record<string, unknown>;\n tenantId?: string | null;\n publishedAt?: Date | null;\n createdAt?: Date;\n updatedAt?: Date;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'content_corrections',\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get', 'create', 'update'] },\n cli: true,\n})\nexport class ContentCorrection extends SmrtObject {\n @foreignKey('Content', { required: true, onDelete: 'CASCADE' })\n contentId = '';\n\n @foreignKey('ContentVersion', { onDelete: 'CASCADE' })\n contentVersionId = '';\n\n @crossPackageRef('@happyvertical/smrt-facts:Fact')\n factId = '';\n\n @crossPackageRef('@happyvertical/smrt-facts:Fact')\n replacementFactId = '';\n correctionType: ContentCorrectionType = 'fact';\n status: ContentCorrectionStatus = 'draft';\n summary = '';\n incorrectText = '';\n correctedText = '';\n publicNote = '';\n metadata = '';\n\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n publishedAt: Date | null = null;\n createdAt = new Date();\n updatedAt = new Date();\n\n constructor(options: ContentCorrectionOptions = {}) {\n super(options);\n if (options.contentId) this.contentId = options.contentId;\n if (options.contentVersionId !== undefined)\n this.contentVersionId = options.contentVersionId;\n if (options.factId !== undefined) this.factId = options.factId;\n if (options.replacementFactId !== undefined)\n this.replacementFactId = options.replacementFactId;\n if (options.correctionType !== undefined)\n this.correctionType = options.correctionType;\n if (options.status !== undefined) this.status = options.status;\n if (options.summary !== undefined) this.summary = options.summary;\n if (options.incorrectText !== undefined)\n this.incorrectText = options.incorrectText;\n if (options.correctedText !== undefined)\n this.correctedText = options.correctedText;\n if (options.publicNote !== undefined) this.publicNote = options.publicNote;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.publishedAt !== undefined)\n this.publishedAt = options.publishedAt;\n if (options.createdAt) this.createdAt = options.createdAt;\n if (options.updatedAt) this.updatedAt = options.updatedAt;\n\n if (options.metadata !== undefined) {\n this.metadata =\n typeof options.metadata === 'string'\n ? options.metadata\n : JSON.stringify(options.metadata);\n }\n }\n\n getMetadata(): Record<string, unknown> {\n try {\n return this.metadata ? JSON.parse(this.metadata) : {};\n } catch {\n return {};\n }\n }\n}\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport { ContentCorrection } from './content-correction';\n\nexport class ContentCorrectionCollection extends SmrtCollection<ContentCorrection> {\n static readonly _itemClass = ContentCorrection;\n\n async listForContent(contentId: string): Promise<ContentCorrection[]> {\n return this.list({\n where: { contentId },\n orderBy: 'created_at DESC',\n });\n }\n\n async getPublishedForContent(\n contentId: string,\n ): Promise<ContentCorrection[]> {\n return this.list({\n where: { contentId, status: 'published' },\n orderBy: 'published_at DESC',\n });\n }\n\n async issue(\n options: ConstructorParameters<typeof ContentCorrection>[0],\n ): Promise<ContentCorrection> {\n if (!options) {\n return this.create({});\n }\n\n return this.create({\n ...options,\n metadata:\n typeof options.metadata === 'string'\n ? options.metadata\n : options.metadata\n ? JSON.stringify(options.metadata)\n : undefined,\n });\n }\n}\n"],"mappings":"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"}