@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
@@ -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
@@ -965,10 +1020,11 @@ keeps the control from misstating the query; `readContentListFilter` stays
965
1020
  value-only and must not drive a control. Choosing any real option replaces every
966
1021
  filter on that column, so the operator is never stuck.
967
1022
 
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.
1023
+ `review` is now offered outright. `deleted` is deliberately not part of the
1024
+ ordinary status vocabulary: the opt-in trash lifecycle locks that predicate
1025
+ and supplies the restore/purge affordances as one inseparable mode.
1026
+ `Content.type` is freeform, so its option list is a display vocabulary rather
1027
+ than the model's domain.
972
1028
 
973
1029
  Everything a restore or a translation refused is reported in one dismissible
974
1030
  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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"}