@happyvertical/smrt-comments 0.60.9 → 0.60.11
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/dist/chunks/participation-CLI-gl0-.js +276 -0
- package/dist/chunks/participation-CLI-gl0-.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/manifest.json +97 -25
- package/dist/participation.js +1 -1
- package/dist/smrt-knowledge.json +3 -3
- package/dist/smrt-knowledge.okf.md +70 -0
- package/package.json +5 -5
- package/dist/chunks/participation-BOKtvJ26.js +0 -276
- package/dist/chunks/participation-BOKtvJ26.js.map +0 -1
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { ObjectRegistry, SmrtCollection, SmrtObject, digestRunOnceContent, field, smrt } from "@happyvertical/smrt-core";
|
|
2
|
+
import { TenantScoped, tenantId, withTenant } from "@happyvertical/smrt-tenancy";
|
|
3
|
+
//#region src/__smrt-register__.ts
|
|
4
|
+
ObjectRegistry.registerPackageManifest(JSON.parse(`{"version":"1.0.0","timestamp":0,"packageName":"@happyvertical/smrt-comments","packageVersion":"0.60.11","objects":{"@happyvertical/smrt-comments:CommentCollection":{"name":"commentcollection","className":"CommentCollection","qualifiedName":"@happyvertical/smrt-comments:CommentCollection","collection":"comments","filePath":"/home/runner/work/smrt/smrt/packages/comments/src/collections/CommentCollection.ts","packageName":"@happyvertical/smrt-comments","fields":{},"methods":{"listForRecord":{"name":"listForRecord","async":true,"parameters":[{"name":"recordType","type":"string","optional":false},{"name":"recordId","type":"string","optional":false}],"returnType":"Promise<Comment[]>","isStatic":false,"isPublic":true}},"decoratorConfig":{"tableName":"record_comments","api":false,"mcp":false,"cli":false},"extends":"SmrtCollection","extendsTypeArg":"Comment","exportName":"CommentCollection","collectionExportName":"CommentCollectionCollection","collectionOf":"@happyvertical/smrt-comments:Comment","inheritedExposure":["api","mcp","cli"],"schema":{"tableName":"record_comments","ddl":"CREATE TABLE IF NOT EXISTS \\"record_comments\\" (\\n \\"_sync_origin_device_id\\" TEXT,\\n \\"_sync_revision\\" INTEGER,\\n \\"id\\" UUID PRIMARY KEY NOT NULL,\\n \\"slug\\" TEXT NOT NULL,\\n \\"context\\" TEXT NOT NULL DEFAULT '',\\n \\"created_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"updated_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"meta_type\\" TEXT NOT NULL,\\n \\"meta_id\\" TEXT NOT NULL,\\n \\"role\\" TEXT NOT NULL,\\n \\"sort_order\\" INTEGER DEFAULT 0,\\n \\"tenant_id\\" UUID NOT NULL,\\n \\"author_user_id\\" UUID NOT NULL,\\n \\"body\\" TEXT NOT NULL,\\n \\"mentions\\" TEXT,\\n \\"idempotency_key\\" TEXT,\\n \\"retry_tenant_id\\" TEXT,\\n \\"request_digest\\" TEXT,\\n \\"parent_comment_id\\" UUID,\\n \\"status\\" TEXT NOT NULL DEFAULT 'visible',\\n \\"participation_snapshot\\" TEXT,\\n \\"edited_at\\" TIMESTAMP\\n);","columns":{"_sync_origin_device_id":{"type":"TEXT"},"_sync_revision":{"type":"INTEGER"},"id":{"type":"UUID","primaryKey":true,"referenceKind":"id","notNull":true},"slug":{"type":"TEXT","notNull":true},"context":{"type":"TEXT","notNull":true,"default":""},"created_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"updated_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"meta_type":{"type":"TEXT","notNull":true,"unique":false},"meta_id":{"type":"TEXT","notNull":true,"unique":false},"role":{"type":"TEXT","notNull":true,"unique":false},"sort_order":{"type":"INTEGER","notNull":false,"unique":false,"default":0},"tenant_id":{"type":"UUID","referenceKind":"tenantId","notNull":true,"unique":false},"author_user_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":true,"unique":false},"body":{"type":"TEXT","notNull":true,"unique":false},"mentions":{"type":"TEXT","notNull":false,"unique":false},"idempotency_key":{"type":"TEXT","notNull":false,"unique":false},"retry_tenant_id":{"type":"TEXT","notNull":false,"unique":false},"request_digest":{"type":"TEXT","notNull":false,"unique":false},"parent_comment_id":{"type":"UUID","referenceKind":"foreignKey","notNull":false,"unique":false},"status":{"type":"TEXT","notNull":true,"unique":false,"default":"visible"},"participation_snapshot":{"type":"TEXT","notNull":false,"unique":false},"edited_at":{"type":"TIMESTAMP","notNull":false,"unique":false}},"indexes":[{"name":"record_comments_slug_context_idx","columns":["tenant_id","slug","context"],"unique":true},{"name":"record_comments_tenant_request_key","columns":["retry_tenant_id","idempotency_key"],"unique":true},{"name":"record_comments_record_created_idx","columns":["tenant_id","meta_type","meta_id","created_at"]},{"name":"record_comments_parent_created_idx","columns":["tenant_id","parent_comment_id","created_at"]},{"name":"record_comments_moderation_idx","columns":["tenant_id","status","created_at"]},{"name":"record_comments_tenant_id_created_at_idx","columns":["tenant_id","created_at"]},{"name":"record_comments_meta_type_meta_id_idx","columns":["meta_type","meta_id"]},{"name":"record_comments_author_user_id_idx","columns":["author_user_id"]},{"name":"record_comments_parent_comment_id_idx","columns":["parent_comment_id"]}],"version":"b3737a6a"},"importPath":"@happyvertical/smrt-comments"},"@happyvertical/smrt-comments:CommentReportCollection":{"name":"commentreportcollection","className":"CommentReportCollection","qualifiedName":"@happyvertical/smrt-comments:CommentReportCollection","collection":"commentreports","filePath":"/home/runner/work/smrt/smrt/packages/comments/src/collections/CommentReportCollection.ts","packageName":"@happyvertical/smrt-comments","fields":{},"methods":{},"decoratorConfig":{"tableName":"comment_reports","api":false,"mcp":false,"cli":false},"extends":"SmrtCollection","extendsTypeArg":"CommentReport","exportName":"CommentReportCollection","collectionExportName":"CommentReportCollectionCollection","collectionOf":"@happyvertical/smrt-comments:CommentReport","inheritedExposure":["api","mcp","cli"],"schema":{"tableName":"comment_reports","ddl":"CREATE TABLE IF NOT EXISTS \\"comment_reports\\" (\\n \\"_sync_origin_device_id\\" TEXT,\\n \\"_sync_revision\\" INTEGER,\\n \\"id\\" UUID PRIMARY KEY NOT NULL,\\n \\"slug\\" TEXT NOT NULL,\\n \\"context\\" TEXT NOT NULL DEFAULT '',\\n \\"created_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"updated_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"tenant_id\\" UUID NOT NULL,\\n \\"comment_id\\" UUID NOT NULL,\\n \\"reporter_user_id\\" UUID NOT NULL,\\n \\"reason\\" TEXT NOT NULL,\\n \\"status\\" TEXT NOT NULL DEFAULT 'open'\\n);","columns":{"_sync_origin_device_id":{"type":"TEXT"},"_sync_revision":{"type":"INTEGER"},"id":{"type":"UUID","primaryKey":true,"referenceKind":"id","notNull":true},"slug":{"type":"TEXT","notNull":true},"context":{"type":"TEXT","notNull":true,"default":""},"created_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"updated_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"tenant_id":{"type":"UUID","referenceKind":"tenantId","notNull":true,"unique":false},"comment_id":{"type":"UUID","referenceKind":"foreignKey","notNull":true,"unique":false},"reporter_user_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":true,"unique":false},"reason":{"type":"TEXT","notNull":true,"unique":false},"status":{"type":"TEXT","notNull":true,"unique":false,"default":"open"}},"indexes":[{"name":"comment_reports_slug_context_idx","columns":["tenant_id","slug","context"],"unique":true},{"name":"comment_reports_actor_target_unique","columns":["tenant_id","reporter_user_id","comment_id"],"unique":true},{"name":"comment_reports_queue_idx","columns":["tenant_id","status","created_at"]},{"name":"comment_reports_tenant_id_created_at_idx","columns":["tenant_id","created_at"]},{"name":"comment_reports_comment_id_idx","columns":["comment_id"]},{"name":"comment_reports_reporter_user_id_idx","columns":["reporter_user_id"]}],"version":"ccf437e5"},"importPath":"@happyvertical/smrt-comments"},"@happyvertical/smrt-comments:CommentRevisionCollection":{"name":"commentrevisioncollection","className":"CommentRevisionCollection","qualifiedName":"@happyvertical/smrt-comments:CommentRevisionCollection","collection":"commentrevisions","filePath":"/home/runner/work/smrt/smrt/packages/comments/src/collections/CommentRevisionCollection.ts","packageName":"@happyvertical/smrt-comments","fields":{},"methods":{},"decoratorConfig":{"tableName":"comment_revisions","api":false,"mcp":false,"cli":false},"extends":"SmrtCollection","extendsTypeArg":"CommentRevision","exportName":"CommentRevisionCollection","collectionExportName":"CommentRevisionCollectionCollection","collectionOf":"@happyvertical/smrt-comments:CommentRevision","inheritedExposure":["api","mcp","cli"],"schema":{"tableName":"comment_revisions","ddl":"CREATE TABLE IF NOT EXISTS \\"comment_revisions\\" (\\n \\"_sync_origin_device_id\\" TEXT,\\n \\"_sync_revision\\" INTEGER,\\n \\"id\\" UUID PRIMARY KEY NOT NULL,\\n \\"slug\\" TEXT NOT NULL,\\n \\"context\\" TEXT NOT NULL DEFAULT '',\\n \\"created_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"updated_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"tenant_id\\" UUID NOT NULL,\\n \\"comment_id\\" UUID NOT NULL,\\n \\"editor_user_id\\" UUID NOT NULL,\\n \\"body\\" TEXT NOT NULL\\n);","columns":{"_sync_origin_device_id":{"type":"TEXT"},"_sync_revision":{"type":"INTEGER"},"id":{"type":"UUID","primaryKey":true,"referenceKind":"id","notNull":true},"slug":{"type":"TEXT","notNull":true},"context":{"type":"TEXT","notNull":true,"default":""},"created_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"updated_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"tenant_id":{"type":"UUID","referenceKind":"tenantId","notNull":true,"unique":false},"comment_id":{"type":"UUID","referenceKind":"foreignKey","notNull":true,"unique":false},"editor_user_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":true,"unique":false},"body":{"type":"TEXT","notNull":true,"unique":false}},"indexes":[{"name":"comment_revisions_slug_context_idx","columns":["tenant_id","slug","context"],"unique":true},{"name":"comment_revisions_tenant_id_created_at_idx","columns":["tenant_id","created_at"]},{"name":"comment_revisions_comment_id_idx","columns":["comment_id"]},{"name":"comment_revisions_editor_user_id_idx","columns":["editor_user_id"]}],"version":"4004817b"},"importPath":"@happyvertical/smrt-comments"},"@happyvertical/smrt-comments:ParticipationPolicyCollection":{"name":"participationpolicycollection","className":"ParticipationPolicyCollection","qualifiedName":"@happyvertical/smrt-comments:ParticipationPolicyCollection","collection":"participationpolicies","filePath":"/home/runner/work/smrt/smrt/packages/comments/src/collections/ParticipationCollection.ts","packageName":"@happyvertical/smrt-comments","fields":{},"methods":{},"decoratorConfig":{"tableName":"participation_policies","api":false,"mcp":false,"cli":false},"extends":"SmrtCollection","extendsTypeArg":"ParticipationPolicy","exportName":"ParticipationPolicyCollection","collectionExportName":"ParticipationPolicyCollectionCollection","collectionOf":"@happyvertical/smrt-comments:ParticipationPolicy","inheritedExposure":["api","mcp","cli"],"schema":{"tableName":"participation_policies","ddl":"CREATE TABLE IF NOT EXISTS \\"participation_policies\\" (\\n \\"_sync_origin_device_id\\" TEXT,\\n \\"_sync_revision\\" INTEGER,\\n \\"id\\" UUID PRIMARY KEY NOT NULL,\\n \\"slug\\" TEXT NOT NULL,\\n \\"context\\" TEXT NOT NULL DEFAULT '',\\n \\"created_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"updated_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"tenant_id\\" UUID NOT NULL,\\n \\"record_type\\" TEXT,\\n \\"record_id\\" TEXT,\\n \\"action\\" TEXT NOT NULL,\\n \\"moderation_mode\\" TEXT NOT NULL DEFAULT 'open',\\n \\"max_actions\\" INTEGER NOT NULL DEFAULT 0,\\n \\"window_seconds\\" INTEGER NOT NULL DEFAULT 0\\n);","columns":{"_sync_origin_device_id":{"type":"TEXT"},"_sync_revision":{"type":"INTEGER"},"id":{"type":"UUID","primaryKey":true,"referenceKind":"id","notNull":true},"slug":{"type":"TEXT","notNull":true},"context":{"type":"TEXT","notNull":true,"default":""},"created_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"updated_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"tenant_id":{"type":"UUID","referenceKind":"tenantId","notNull":true,"unique":false},"record_type":{"type":"TEXT","notNull":false,"unique":false},"record_id":{"type":"TEXT","notNull":false,"unique":false},"action":{"type":"TEXT","notNull":true,"unique":false},"moderation_mode":{"type":"TEXT","notNull":true,"unique":false,"default":"open"},"max_actions":{"type":"INTEGER","notNull":true,"unique":false,"default":0},"window_seconds":{"type":"INTEGER","notNull":true,"unique":false,"default":0}},"indexes":[{"name":"participation_policies_slug_context_idx","columns":["tenant_id","slug","context"],"unique":true},{"name":"participation_policies_tenant_id_created_at_idx","columns":["tenant_id","created_at"]}],"version":"0aa39678"},"importPath":"@happyvertical/smrt-comments"},"@happyvertical/smrt-comments:ParticipationBanCollection":{"name":"participationbancollection","className":"ParticipationBanCollection","qualifiedName":"@happyvertical/smrt-comments:ParticipationBanCollection","collection":"participationbans","filePath":"/home/runner/work/smrt/smrt/packages/comments/src/collections/ParticipationCollection.ts","packageName":"@happyvertical/smrt-comments","fields":{},"methods":{},"decoratorConfig":{"tableName":"participation_bans","api":false,"mcp":false,"cli":false},"extends":"SmrtCollection","extendsTypeArg":"ParticipationBan","exportName":"ParticipationBanCollection","collectionExportName":"ParticipationBanCollectionCollection","collectionOf":"@happyvertical/smrt-comments:ParticipationBan","inheritedExposure":["api","mcp","cli"],"schema":{"tableName":"participation_bans","ddl":"CREATE TABLE IF NOT EXISTS \\"participation_bans\\" (\\n \\"_sync_origin_device_id\\" TEXT,\\n \\"_sync_revision\\" INTEGER,\\n \\"id\\" UUID PRIMARY KEY NOT NULL,\\n \\"slug\\" TEXT NOT NULL,\\n \\"context\\" TEXT NOT NULL DEFAULT '',\\n \\"created_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"updated_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"tenant_id\\" UUID NOT NULL,\\n \\"actor_user_id\\" TEXT NOT NULL,\\n \\"record_type\\" TEXT,\\n \\"record_id\\" TEXT,\\n \\"reason\\" TEXT NOT NULL,\\n \\"expires_at\\" TIMESTAMP\\n);","columns":{"_sync_origin_device_id":{"type":"TEXT"},"_sync_revision":{"type":"INTEGER"},"id":{"type":"UUID","primaryKey":true,"referenceKind":"id","notNull":true},"slug":{"type":"TEXT","notNull":true},"context":{"type":"TEXT","notNull":true,"default":""},"created_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"updated_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"tenant_id":{"type":"UUID","referenceKind":"tenantId","notNull":true,"unique":false},"actor_user_id":{"type":"TEXT","notNull":true,"unique":false},"record_type":{"type":"TEXT","notNull":false,"unique":false},"record_id":{"type":"TEXT","notNull":false,"unique":false},"reason":{"type":"TEXT","notNull":true,"unique":false},"expires_at":{"type":"TIMESTAMP","notNull":false,"unique":false}},"indexes":[{"name":"participation_bans_slug_context_idx","columns":["tenant_id","slug","context"],"unique":true},{"name":"participation_bans_tenant_id_created_at_idx","columns":["tenant_id","created_at"]}],"version":"39a82352"},"importPath":"@happyvertical/smrt-comments"},"@happyvertical/smrt-comments:ParticipationRateBucketCollection":{"name":"participationratebucketcollection","className":"ParticipationRateBucketCollection","qualifiedName":"@happyvertical/smrt-comments:ParticipationRateBucketCollection","collection":"participationratebuckets","filePath":"/home/runner/work/smrt/smrt/packages/comments/src/collections/ParticipationCollection.ts","packageName":"@happyvertical/smrt-comments","fields":{},"methods":{},"decoratorConfig":{"tableName":"participation_rate_buckets","api":false,"mcp":false,"cli":false},"extends":"SmrtCollection","extendsTypeArg":"ParticipationRateBucket","exportName":"ParticipationRateBucketCollection","collectionExportName":"ParticipationRateBucketCollectionCollection","collectionOf":"@happyvertical/smrt-comments:ParticipationRateBucket","inheritedExposure":["api","mcp","cli"],"schema":{"tableName":"participation_rate_buckets","ddl":"CREATE TABLE IF NOT EXISTS \\"participation_rate_buckets\\" (\\n \\"_sync_origin_device_id\\" TEXT,\\n \\"_sync_revision\\" INTEGER,\\n \\"id\\" UUID PRIMARY KEY NOT NULL,\\n \\"slug\\" TEXT NOT NULL,\\n \\"context\\" TEXT NOT NULL DEFAULT '',\\n \\"created_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"updated_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"tenant_id\\" UUID NOT NULL,\\n \\"bucket_key\\" TEXT NOT NULL,\\n \\"action_count\\" INTEGER NOT NULL DEFAULT 0\\n);","columns":{"_sync_origin_device_id":{"type":"TEXT"},"_sync_revision":{"type":"INTEGER"},"id":{"type":"UUID","primaryKey":true,"referenceKind":"id","notNull":true},"slug":{"type":"TEXT","notNull":true},"context":{"type":"TEXT","notNull":true,"default":""},"created_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"updated_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"tenant_id":{"type":"UUID","referenceKind":"tenantId","notNull":true,"unique":false},"bucket_key":{"type":"TEXT","notNull":true,"unique":false},"action_count":{"type":"INTEGER","notNull":true,"unique":false,"default":0}},"indexes":[{"name":"participation_rate_buckets_slug_context_idx","columns":["tenant_id","slug","context"],"unique":true},{"name":"participation_rate_bucket_unique","columns":["tenant_id","bucket_key"],"unique":true},{"name":"participation_rate_buckets_tenant_id_created_at_idx","columns":["tenant_id","created_at"]}],"version":"73800b88"},"importPath":"@happyvertical/smrt-comments"},"@happyvertical/smrt-comments:Comment":{"name":"comment","className":"Comment","qualifiedName":"@happyvertical/smrt-comments:Comment","collection":"comments","filePath":"/home/runner/work/smrt/smrt/packages/comments/src/models/Comment.ts","packageName":"@happyvertical/smrt-comments","fields":{"metaType":{"type":"text","required":true,"_meta":{"required":true}},"metaId":{"type":"text","required":true,"_meta":{"required":true}},"role":{"type":"text","required":true,"_meta":{"required":true}},"sortOrder":{"type":"integer","required":false,"default":0},"tenantId":{"type":"text","required":true,"_meta":{"sqlType":"UUID","__tenancy":{"isTenantIdField":true,"autoFilter":true,"required":true,"autoPopulate":true,"nullable":false,"mode":"required","field":"tenantId","allowSuperAdminBypass":false}}},"authorUserId":{"type":"crossPackageRef","required":true,"related":"@happyvertical/smrt-users:User"},"body":{"type":"text","required":true,"_meta":{"required":true}},"mentions":{"type":"text","required":false},"idempotencyKey":{"type":"text","required":false,"_meta":{"nullable":true,"readonly":true},"readonly":true},"retryTenantId":{"type":"text","required":false,"_meta":{"nullable":true,"readonly":true},"readonly":true},"requestDigest":{"type":"text","required":false,"_meta":{"nullable":true,"readonly":true},"readonly":true},"parentCommentId":{"type":"foreignKey","required":false,"related":"Comment","_meta":{"nullable":true,"constraint":false}},"status":{"type":"text","required":true,"default":"visible","_meta":{"required":true},"enum":["visible","hidden","pending","deleted"]},"participationSnapshot":{"type":"text","required":false,"_meta":{"nullable":true,"readonly":true},"readonly":true},"editedAt":{"type":"datetime","required":false,"_meta":{"nullable":true,"readonly":true},"readonly":true}},"methods":{"hydrate":{"name":"hydrate","async":true,"parameters":[],"returnType":"Promise<T | null>","isStatic":false,"isPublic":true}},"decoratorConfig":{"tableName":"record_comments","api":false,"mcp":false,"cli":false,"indexes":[{"name":"record_comments_tenant_request_key","columns":["retryTenantId","idempotencyKey"],"unique":true},{"name":"record_comments_record_created_idx","columns":["tenantId","metaType","metaId","createdAt"]},{"name":"record_comments_parent_created_idx","columns":["tenantId","parentCommentId","createdAt"]},{"name":"record_comments_moderation_idx","columns":["tenantId","status","createdAt"]}],"tenantScoped":{"mode":"required"},"conflictColumns":["tenant_id","slug","context"]},"extends":"SmrtPolymorphicAssociation","exportName":"Comment","collectionExportName":"CommentCollection","validationRules":[{"field":"metaType","rule":"required","fieldType":"text"},{"field":"metaId","rule":"required","fieldType":"text"},{"field":"role","rule":"required","fieldType":"text"},{"field":"authorUserId","rule":"required","fieldType":"crossPackageRef"},{"field":"body","rule":"required","fieldType":"text"},{"field":"status","rule":"required","fieldType":"text"}],"schema":{"tableName":"record_comments","ddl":"CREATE TABLE IF NOT EXISTS \\"record_comments\\" (\\n \\"_sync_origin_device_id\\" TEXT,\\n \\"_sync_revision\\" INTEGER,\\n \\"id\\" UUID PRIMARY KEY NOT NULL,\\n \\"slug\\" TEXT NOT NULL,\\n \\"context\\" TEXT NOT NULL DEFAULT '',\\n \\"created_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"updated_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"meta_type\\" TEXT NOT NULL,\\n \\"meta_id\\" TEXT NOT NULL,\\n \\"role\\" TEXT NOT NULL,\\n \\"sort_order\\" INTEGER DEFAULT 0,\\n \\"tenant_id\\" UUID NOT NULL,\\n \\"author_user_id\\" UUID NOT NULL,\\n \\"body\\" TEXT NOT NULL,\\n \\"mentions\\" TEXT,\\n \\"idempotency_key\\" TEXT,\\n \\"retry_tenant_id\\" TEXT,\\n \\"request_digest\\" TEXT,\\n \\"parent_comment_id\\" UUID,\\n \\"status\\" TEXT NOT NULL DEFAULT 'visible',\\n \\"participation_snapshot\\" TEXT,\\n \\"edited_at\\" TIMESTAMP\\n);","columns":{"_sync_origin_device_id":{"type":"TEXT"},"_sync_revision":{"type":"INTEGER"},"id":{"type":"UUID","primaryKey":true,"referenceKind":"id","notNull":true},"slug":{"type":"TEXT","notNull":true},"context":{"type":"TEXT","notNull":true,"default":""},"created_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"updated_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"meta_type":{"type":"TEXT","notNull":true,"unique":false},"meta_id":{"type":"TEXT","notNull":true,"unique":false},"role":{"type":"TEXT","notNull":true,"unique":false},"sort_order":{"type":"INTEGER","notNull":false,"unique":false,"default":0},"tenant_id":{"type":"UUID","referenceKind":"tenantId","notNull":true,"unique":false},"author_user_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":true,"unique":false},"body":{"type":"TEXT","notNull":true,"unique":false},"mentions":{"type":"TEXT","notNull":false,"unique":false},"idempotency_key":{"type":"TEXT","notNull":false,"unique":false},"retry_tenant_id":{"type":"TEXT","notNull":false,"unique":false},"request_digest":{"type":"TEXT","notNull":false,"unique":false},"parent_comment_id":{"type":"UUID","referenceKind":"foreignKey","notNull":false,"unique":false},"status":{"type":"TEXT","notNull":true,"unique":false,"default":"visible"},"participation_snapshot":{"type":"TEXT","notNull":false,"unique":false},"edited_at":{"type":"TIMESTAMP","notNull":false,"unique":false}},"indexes":[{"name":"record_comments_slug_context_idx","columns":["tenant_id","slug","context"],"unique":true},{"name":"record_comments_tenant_request_key","columns":["retry_tenant_id","idempotency_key"],"unique":true},{"name":"record_comments_record_created_idx","columns":["tenant_id","meta_type","meta_id","created_at"]},{"name":"record_comments_parent_created_idx","columns":["tenant_id","parent_comment_id","created_at"]},{"name":"record_comments_moderation_idx","columns":["tenant_id","status","created_at"]},{"name":"record_comments_tenant_id_created_at_idx","columns":["tenant_id","created_at"]},{"name":"record_comments_meta_type_meta_id_idx","columns":["meta_type","meta_id"]},{"name":"record_comments_author_user_id_idx","columns":["author_user_id"]},{"name":"record_comments_parent_comment_id_idx","columns":["parent_comment_id"]}],"version":"b3737a6a"},"importPath":"@happyvertical/smrt-comments"},"@happyvertical/smrt-comments:CommentReport":{"name":"commentreport","className":"CommentReport","qualifiedName":"@happyvertical/smrt-comments:CommentReport","collection":"commentreports","description":"A tenant-local report; moderation resolution never deletes the report.","filePath":"/home/runner/work/smrt/smrt/packages/comments/src/models/CommentReport.ts","packageName":"@happyvertical/smrt-comments","fields":{"tenantId":{"type":"text","required":true,"_meta":{"sqlType":"UUID","__tenancy":{"isTenantIdField":true,"autoFilter":true,"required":true,"autoPopulate":true,"nullable":false,"mode":"required","field":"tenantId","allowSuperAdminBypass":false}}},"commentId":{"type":"foreignKey","required":true,"related":"Comment","_meta":{"required":true,"constraint":false}},"reporterUserId":{"type":"crossPackageRef","required":true,"related":"@happyvertical/smrt-users:User"},"reason":{"type":"text","required":true,"_meta":{"required":true}},"status":{"type":"text","required":true,"default":"open","_meta":{"required":true},"enum":["open","resolved","dismissed"]}},"methods":{},"decoratorConfig":{"tableName":"comment_reports","api":false,"mcp":false,"cli":false,"indexes":[{"name":"comment_reports_actor_target_unique","columns":["tenantId","reporterUserId","commentId"],"unique":true},{"name":"comment_reports_queue_idx","columns":["tenantId","status","createdAt"]}],"tenantScoped":{"mode":"required"},"conflictColumns":["tenant_id","slug","context"]},"extends":"SmrtObject","exportName":"CommentReport","collectionExportName":"CommentReportCollection","validationRules":[{"field":"commentId","rule":"required","fieldType":"foreignKey"},{"field":"reporterUserId","rule":"required","fieldType":"crossPackageRef"},{"field":"reason","rule":"required","fieldType":"text"},{"field":"status","rule":"required","fieldType":"text"}],"schema":{"tableName":"comment_reports","ddl":"CREATE TABLE IF NOT EXISTS \\"comment_reports\\" (\\n \\"_sync_origin_device_id\\" TEXT,\\n \\"_sync_revision\\" INTEGER,\\n \\"id\\" UUID PRIMARY KEY NOT NULL,\\n \\"slug\\" TEXT NOT NULL,\\n \\"context\\" TEXT NOT NULL DEFAULT '',\\n \\"created_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"updated_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"tenant_id\\" UUID NOT NULL,\\n \\"comment_id\\" UUID NOT NULL,\\n \\"reporter_user_id\\" UUID NOT NULL,\\n \\"reason\\" TEXT NOT NULL,\\n \\"status\\" TEXT NOT NULL DEFAULT 'open'\\n);","columns":{"_sync_origin_device_id":{"type":"TEXT"},"_sync_revision":{"type":"INTEGER"},"id":{"type":"UUID","primaryKey":true,"referenceKind":"id","notNull":true},"slug":{"type":"TEXT","notNull":true},"context":{"type":"TEXT","notNull":true,"default":""},"created_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"updated_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"tenant_id":{"type":"UUID","referenceKind":"tenantId","notNull":true,"unique":false},"comment_id":{"type":"UUID","referenceKind":"foreignKey","notNull":true,"unique":false},"reporter_user_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":true,"unique":false},"reason":{"type":"TEXT","notNull":true,"unique":false},"status":{"type":"TEXT","notNull":true,"unique":false,"default":"open"}},"indexes":[{"name":"comment_reports_slug_context_idx","columns":["tenant_id","slug","context"],"unique":true},{"name":"comment_reports_actor_target_unique","columns":["tenant_id","reporter_user_id","comment_id"],"unique":true},{"name":"comment_reports_queue_idx","columns":["tenant_id","status","created_at"]},{"name":"comment_reports_tenant_id_created_at_idx","columns":["tenant_id","created_at"]},{"name":"comment_reports_comment_id_idx","columns":["comment_id"]},{"name":"comment_reports_reporter_user_id_idx","columns":["reporter_user_id"]}],"version":"ccf437e5"},"importPath":"@happyvertical/smrt-comments"},"@happyvertical/smrt-comments:CommentRevision":{"name":"commentrevision","className":"CommentRevision","qualifiedName":"@happyvertical/smrt-comments:CommentRevision","collection":"commentrevisions","description":"Immutable prior body retained whenever an author edits a comment.","filePath":"/home/runner/work/smrt/smrt/packages/comments/src/models/CommentRevision.ts","packageName":"@happyvertical/smrt-comments","fields":{"tenantId":{"type":"text","required":true,"_meta":{"sqlType":"UUID","__tenancy":{"isTenantIdField":true,"autoFilter":true,"required":true,"autoPopulate":true,"nullable":false,"mode":"required","field":"tenantId","allowSuperAdminBypass":false}}},"commentId":{"type":"foreignKey","required":true,"related":"Comment","_meta":{"required":true,"constraint":false}},"editorUserId":{"type":"crossPackageRef","required":true,"related":"@happyvertical/smrt-users:User"},"body":{"type":"text","required":true,"_meta":{"required":true}}},"methods":{},"decoratorConfig":{"tableName":"comment_revisions","api":false,"mcp":false,"cli":false,"tenantScoped":{"mode":"required"},"conflictColumns":["tenant_id","slug","context"]},"extends":"SmrtObject","exportName":"CommentRevision","collectionExportName":"CommentRevisionCollection","validationRules":[{"field":"commentId","rule":"required","fieldType":"foreignKey"},{"field":"editorUserId","rule":"required","fieldType":"crossPackageRef"},{"field":"body","rule":"required","fieldType":"text"}],"schema":{"tableName":"comment_revisions","ddl":"CREATE TABLE IF NOT EXISTS \\"comment_revisions\\" (\\n \\"_sync_origin_device_id\\" TEXT,\\n \\"_sync_revision\\" INTEGER,\\n \\"id\\" UUID PRIMARY KEY NOT NULL,\\n \\"slug\\" TEXT NOT NULL,\\n \\"context\\" TEXT NOT NULL DEFAULT '',\\n \\"created_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"updated_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"tenant_id\\" UUID NOT NULL,\\n \\"comment_id\\" UUID NOT NULL,\\n \\"editor_user_id\\" UUID NOT NULL,\\n \\"body\\" TEXT NOT NULL\\n);","columns":{"_sync_origin_device_id":{"type":"TEXT"},"_sync_revision":{"type":"INTEGER"},"id":{"type":"UUID","primaryKey":true,"referenceKind":"id","notNull":true},"slug":{"type":"TEXT","notNull":true},"context":{"type":"TEXT","notNull":true,"default":""},"created_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"updated_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"tenant_id":{"type":"UUID","referenceKind":"tenantId","notNull":true,"unique":false},"comment_id":{"type":"UUID","referenceKind":"foreignKey","notNull":true,"unique":false},"editor_user_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":true,"unique":false},"body":{"type":"TEXT","notNull":true,"unique":false}},"indexes":[{"name":"comment_revisions_slug_context_idx","columns":["tenant_id","slug","context"],"unique":true},{"name":"comment_revisions_tenant_id_created_at_idx","columns":["tenant_id","created_at"]},{"name":"comment_revisions_comment_id_idx","columns":["comment_id"]},{"name":"comment_revisions_editor_user_id_idx","columns":["editor_user_id"]}],"version":"4004817b"},"importPath":"@happyvertical/smrt-comments"},"@happyvertical/smrt-comments:ParticipationBan":{"name":"participationban","className":"ParticipationBan","qualifiedName":"@happyvertical/smrt-comments:ParticipationBan","collection":"participationbans","description":"Tenant-local, reusable participation ban; a blank target applies tenant-wide.","filePath":"/home/runner/work/smrt/smrt/packages/comments/src/models/ParticipationBan.ts","packageName":"@happyvertical/smrt-comments","fields":{"tenantId":{"type":"text","required":true,"_meta":{"sqlType":"UUID","__tenancy":{"isTenantIdField":true,"autoFilter":true,"required":true,"autoPopulate":true,"nullable":false,"mode":"required","field":"tenantId","allowSuperAdminBypass":false}}},"actorUserId":{"type":"text","required":true,"_meta":{"required":true}},"recordType":{"type":"text","required":false},"recordId":{"type":"text","required":false},"reason":{"type":"text","required":true,"_meta":{"required":true}},"expiresAt":{"type":"datetime","required":false,"_meta":{"nullable":true}}},"methods":{},"decoratorConfig":{"tableName":"participation_bans","api":false,"mcp":false,"cli":false,"tenantScoped":{"mode":"required"},"conflictColumns":["tenant_id","slug","context"]},"extends":"SmrtObject","exportName":"ParticipationBan","collectionExportName":"ParticipationBanCollection","validationRules":[{"field":"actorUserId","rule":"required","fieldType":"text"},{"field":"reason","rule":"required","fieldType":"text"}],"schema":{"tableName":"participation_bans","ddl":"CREATE TABLE IF NOT EXISTS \\"participation_bans\\" (\\n \\"_sync_origin_device_id\\" TEXT,\\n \\"_sync_revision\\" INTEGER,\\n \\"id\\" UUID PRIMARY KEY NOT NULL,\\n \\"slug\\" TEXT NOT NULL,\\n \\"context\\" TEXT NOT NULL DEFAULT '',\\n \\"created_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"updated_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"tenant_id\\" UUID NOT NULL,\\n \\"actor_user_id\\" TEXT NOT NULL,\\n \\"record_type\\" TEXT,\\n \\"record_id\\" TEXT,\\n \\"reason\\" TEXT NOT NULL,\\n \\"expires_at\\" TIMESTAMP\\n);","columns":{"_sync_origin_device_id":{"type":"TEXT"},"_sync_revision":{"type":"INTEGER"},"id":{"type":"UUID","primaryKey":true,"referenceKind":"id","notNull":true},"slug":{"type":"TEXT","notNull":true},"context":{"type":"TEXT","notNull":true,"default":""},"created_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"updated_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"tenant_id":{"type":"UUID","referenceKind":"tenantId","notNull":true,"unique":false},"actor_user_id":{"type":"TEXT","notNull":true,"unique":false},"record_type":{"type":"TEXT","notNull":false,"unique":false},"record_id":{"type":"TEXT","notNull":false,"unique":false},"reason":{"type":"TEXT","notNull":true,"unique":false},"expires_at":{"type":"TIMESTAMP","notNull":false,"unique":false}},"indexes":[{"name":"participation_bans_slug_context_idx","columns":["tenant_id","slug","context"],"unique":true},{"name":"participation_bans_tenant_id_created_at_idx","columns":["tenant_id","created_at"]}],"version":"39a82352"},"importPath":"@happyvertical/smrt-comments"},"@happyvertical/smrt-comments:ParticipationPolicy":{"name":"participationpolicy","className":"ParticipationPolicy","qualifiedName":"@happyvertical/smrt-comments:ParticipationPolicy","collection":"participationpolicies","description":"A generic tenant default or exact-record override for participation actions.","filePath":"/home/runner/work/smrt/smrt/packages/comments/src/models/ParticipationPolicy.ts","packageName":"@happyvertical/smrt-comments","fields":{"tenantId":{"type":"text","required":true,"_meta":{"sqlType":"UUID","__tenancy":{"isTenantIdField":true,"autoFilter":true,"required":true,"autoPopulate":true,"nullable":false,"mode":"required","field":"tenantId","allowSuperAdminBypass":false}}},"recordType":{"type":"text","required":false},"recordId":{"type":"text","required":false},"action":{"type":"text","required":true,"_meta":{"required":true}},"moderationMode":{"type":"text","required":true,"default":"open","_meta":{"required":true},"enum":["open","pre_moderated","members_only","closed"]},"maxActions":{"type":"integer","required":true,"default":0,"_meta":{"required":true}},"windowSeconds":{"type":"integer","required":true,"default":0,"_meta":{"required":true}}},"methods":{},"decoratorConfig":{"tableName":"participation_policies","api":false,"mcp":false,"cli":false,"tenantScoped":{"mode":"required"},"conflictColumns":["tenant_id","slug","context"]},"extends":"SmrtObject","exportName":"ParticipationPolicy","collectionExportName":"ParticipationPolicyCollection","validationRules":[{"field":"action","rule":"required","fieldType":"text"},{"field":"moderationMode","rule":"required","fieldType":"text"},{"field":"maxActions","rule":"required","fieldType":"integer"},{"field":"windowSeconds","rule":"required","fieldType":"integer"}],"schema":{"tableName":"participation_policies","ddl":"CREATE TABLE IF NOT EXISTS \\"participation_policies\\" (\\n \\"_sync_origin_device_id\\" TEXT,\\n \\"_sync_revision\\" INTEGER,\\n \\"id\\" UUID PRIMARY KEY NOT NULL,\\n \\"slug\\" TEXT NOT NULL,\\n \\"context\\" TEXT NOT NULL DEFAULT '',\\n \\"created_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"updated_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"tenant_id\\" UUID NOT NULL,\\n \\"record_type\\" TEXT,\\n \\"record_id\\" TEXT,\\n \\"action\\" TEXT NOT NULL,\\n \\"moderation_mode\\" TEXT NOT NULL DEFAULT 'open',\\n \\"max_actions\\" INTEGER NOT NULL DEFAULT 0,\\n \\"window_seconds\\" INTEGER NOT NULL DEFAULT 0\\n);","columns":{"_sync_origin_device_id":{"type":"TEXT"},"_sync_revision":{"type":"INTEGER"},"id":{"type":"UUID","primaryKey":true,"referenceKind":"id","notNull":true},"slug":{"type":"TEXT","notNull":true},"context":{"type":"TEXT","notNull":true,"default":""},"created_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"updated_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"tenant_id":{"type":"UUID","referenceKind":"tenantId","notNull":true,"unique":false},"record_type":{"type":"TEXT","notNull":false,"unique":false},"record_id":{"type":"TEXT","notNull":false,"unique":false},"action":{"type":"TEXT","notNull":true,"unique":false},"moderation_mode":{"type":"TEXT","notNull":true,"unique":false,"default":"open"},"max_actions":{"type":"INTEGER","notNull":true,"unique":false,"default":0},"window_seconds":{"type":"INTEGER","notNull":true,"unique":false,"default":0}},"indexes":[{"name":"participation_policies_slug_context_idx","columns":["tenant_id","slug","context"],"unique":true},{"name":"participation_policies_tenant_id_created_at_idx","columns":["tenant_id","created_at"]}],"version":"0aa39678"},"importPath":"@happyvertical/smrt-comments"},"@happyvertical/smrt-comments:ParticipationRateBucket":{"name":"participationratebucket","className":"ParticipationRateBucket","qualifiedName":"@happyvertical/smrt-comments:ParticipationRateBucket","collection":"participationratebuckets","description":"Service-owned fixed-window counter. Never delete a currently active bucket.","filePath":"/home/runner/work/smrt/smrt/packages/comments/src/models/ParticipationRateBucket.ts","packageName":"@happyvertical/smrt-comments","fields":{"tenantId":{"type":"text","required":true,"_meta":{"sqlType":"UUID","__tenancy":{"isTenantIdField":true,"autoFilter":true,"required":true,"autoPopulate":true,"nullable":false,"mode":"required","field":"tenantId","allowSuperAdminBypass":false}}},"bucketKey":{"type":"text","required":true,"_meta":{"required":true}},"actionCount":{"type":"integer","required":true,"default":0,"_meta":{"required":true}}},"methods":{},"decoratorConfig":{"tableName":"participation_rate_buckets","api":false,"mcp":false,"cli":false,"indexes":[{"name":"participation_rate_bucket_unique","columns":["tenantId","bucketKey"],"unique":true}],"tenantScoped":{"mode":"required"},"conflictColumns":["tenant_id","slug","context"]},"extends":"SmrtObject","exportName":"ParticipationRateBucket","collectionExportName":"ParticipationRateBucketCollection","validationRules":[{"field":"bucketKey","rule":"required","fieldType":"text"},{"field":"actionCount","rule":"required","fieldType":"integer"}],"schema":{"tableName":"participation_rate_buckets","ddl":"CREATE TABLE IF NOT EXISTS \\"participation_rate_buckets\\" (\\n \\"_sync_origin_device_id\\" TEXT,\\n \\"_sync_revision\\" INTEGER,\\n \\"id\\" UUID PRIMARY KEY NOT NULL,\\n \\"slug\\" TEXT NOT NULL,\\n \\"context\\" TEXT NOT NULL DEFAULT '',\\n \\"created_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"updated_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"tenant_id\\" UUID NOT NULL,\\n \\"bucket_key\\" TEXT NOT NULL,\\n \\"action_count\\" INTEGER NOT NULL DEFAULT 0\\n);","columns":{"_sync_origin_device_id":{"type":"TEXT"},"_sync_revision":{"type":"INTEGER"},"id":{"type":"UUID","primaryKey":true,"referenceKind":"id","notNull":true},"slug":{"type":"TEXT","notNull":true},"context":{"type":"TEXT","notNull":true,"default":""},"created_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"updated_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"tenant_id":{"type":"UUID","referenceKind":"tenantId","notNull":true,"unique":false},"bucket_key":{"type":"TEXT","notNull":true,"unique":false},"action_count":{"type":"INTEGER","notNull":true,"unique":false,"default":0}},"indexes":[{"name":"participation_rate_buckets_slug_context_idx","columns":["tenant_id","slug","context"],"unique":true},{"name":"participation_rate_bucket_unique","columns":["tenant_id","bucket_key"],"unique":true},{"name":"participation_rate_buckets_tenant_id_created_at_idx","columns":["tenant_id","created_at"]}],"version":"73800b88"},"importPath":"@happyvertical/smrt-comments"}},"moduleType":"smrt","recipes":[{"id":"comments.records","className":"RecordCommentsRecipe","label":"Record comments","summary":"Discuss work directly on the records it concerns.","synonyms":["comments","mentions","discussion"],"models":["@happyvertical/smrt-comments:Comment","@happyvertical/smrt-comments:CommentRevision","@happyvertical/smrt-comments:CommentReport","@happyvertical/smrt-comments:ParticipationPolicy","@happyvertical/smrt-comments:ParticipationBan","@happyvertical/smrt-comments:ParticipationRateBucket"],"nav":[],"requires":[],"group":{"id":"comments","label":"Comments","summary":"Keep discussions alongside the records your team works on."},"help":{"markdown":"## Overview\\n\\nRecord comments keep a discussion next to the work it concerns. Open a record\\nyou can access to read its discussion or add a comment. Comments are part of\\nthe record page, rather than a separate list of every discussion.\\n\\n## Tasks\\n\\n### Add a comment\\n\\nOpen the record, find Comments, write your message and choose Post comment.\\nYour app controls who can read the record and who can contribute to its discussion.\\n\\n### Mention someone\\n\\nIf your app provides mention selection, choose a teammate who can access the\\nrecord. When mention notifications are connected, that person receives a\\nnotification. Typing a name alone does not select a recipient.\\n\\n### Check a failed post\\n\\nIf posting reports an error, refresh the discussion before trying again. A\\ncomment may have been saved even when its mention notification could not be\\ndelivered. If it appears in the discussion, do not post it a second time.\\n","fieldRefs":[]},"surfaces":[{"kind":"playground","export":"@happyvertical/smrt-comments/svelte#RecordCommentsPlayground","label":"Record discussion preview"}],"runtime":"both","demoSeed":{"export":"@happyvertical/smrt-comments/svelte#recordCommentsDemo"},"demo":{"mode":"server","reasons":["Needs a server because its package depends on @happyvertical/smrt-tenancy, which do not build for a browser (#3624)."]}}],"smrtDependencies":["@happyvertical/smrt-core","@happyvertical/smrt-messages","@happyvertical/smrt-tenancy"],"browser":{"status":"server-only","issues":["#3624"],"via":["@happyvertical/smrt-tenancy"]},"stampsConstructors":true}`));
|
|
5
|
+
//#endregion
|
|
6
|
+
//#region src/models/ParticipationBan.ts
|
|
7
|
+
var __defProp$2 = Object.defineProperty;
|
|
8
|
+
var __getOwnPropDesc$2 = Object.getOwnPropertyDescriptor;
|
|
9
|
+
var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, {
|
|
10
|
+
enumerable: true,
|
|
11
|
+
configurable: true,
|
|
12
|
+
writable: true,
|
|
13
|
+
value
|
|
14
|
+
}) : obj[key] = value;
|
|
15
|
+
var __decorateClass$2 = (decorators, target, key, kind) => {
|
|
16
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$2(target, key) : target;
|
|
17
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
18
|
+
if (kind && result) __defProp$2(target, key, result);
|
|
19
|
+
return result;
|
|
20
|
+
};
|
|
21
|
+
var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
22
|
+
var ParticipationBan = class extends SmrtObject {
|
|
23
|
+
tenantId = "";
|
|
24
|
+
actorUserId = "";
|
|
25
|
+
recordType = "";
|
|
26
|
+
recordId = "";
|
|
27
|
+
reason = "";
|
|
28
|
+
expiresAt = null;
|
|
29
|
+
async validateBeforeSave() {
|
|
30
|
+
for (const identity of [
|
|
31
|
+
this.tenantId,
|
|
32
|
+
this.actorUserId,
|
|
33
|
+
this.recordId
|
|
34
|
+
]) if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(identity) && identity !== identity.toLowerCase()) throw new TypeError("Participation administrative UUIDs must be lowercase.");
|
|
35
|
+
if (Boolean(this.recordType) !== Boolean(this.recordId)) throw new TypeError("Participation target requires both recordType and recordId.");
|
|
36
|
+
await super.validateBeforeSave();
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
__publicField$2(ParticipationBan, "__smrtPackage__", "@happyvertical/smrt-comments");
|
|
40
|
+
__decorateClass$2([tenantId()], ParticipationBan.prototype, "tenantId", 2);
|
|
41
|
+
__decorateClass$2([field({ required: true })], ParticipationBan.prototype, "actorUserId", 2);
|
|
42
|
+
__decorateClass$2([field()], ParticipationBan.prototype, "recordType", 2);
|
|
43
|
+
__decorateClass$2([field()], ParticipationBan.prototype, "recordId", 2);
|
|
44
|
+
__decorateClass$2([field({ required: true })], ParticipationBan.prototype, "reason", 2);
|
|
45
|
+
__decorateClass$2([field({
|
|
46
|
+
type: "datetime",
|
|
47
|
+
nullable: true
|
|
48
|
+
})], ParticipationBan.prototype, "expiresAt", 2);
|
|
49
|
+
ParticipationBan = __decorateClass$2([TenantScoped({ mode: "required" }), smrt({
|
|
50
|
+
tableName: "participation_bans",
|
|
51
|
+
api: false,
|
|
52
|
+
mcp: false,
|
|
53
|
+
cli: false
|
|
54
|
+
})], ParticipationBan);
|
|
55
|
+
//#endregion
|
|
56
|
+
//#region src/models/ParticipationPolicy.ts
|
|
57
|
+
var __defProp$1 = Object.defineProperty;
|
|
58
|
+
var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
|
|
59
|
+
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, {
|
|
60
|
+
enumerable: true,
|
|
61
|
+
configurable: true,
|
|
62
|
+
writable: true,
|
|
63
|
+
value
|
|
64
|
+
}) : obj[key] = value;
|
|
65
|
+
var __decorateClass$1 = (decorators, target, key, kind) => {
|
|
66
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$1(target, key) : target;
|
|
67
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
68
|
+
if (kind && result) __defProp$1(target, key, result);
|
|
69
|
+
return result;
|
|
70
|
+
};
|
|
71
|
+
var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
72
|
+
var ParticipationPolicy = class extends SmrtObject {
|
|
73
|
+
tenantId = "";
|
|
74
|
+
recordType = "";
|
|
75
|
+
recordId = "";
|
|
76
|
+
action = "comment";
|
|
77
|
+
moderationMode = "open";
|
|
78
|
+
maxActions = 0;
|
|
79
|
+
windowSeconds = 0;
|
|
80
|
+
async validateBeforeSave() {
|
|
81
|
+
for (const identity of [this.tenantId, this.recordId]) if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(identity) && identity !== identity.toLowerCase()) throw new TypeError("Participation administrative UUIDs must be lowercase.");
|
|
82
|
+
if (Boolean(this.recordType) !== Boolean(this.recordId)) throw new TypeError("Participation target requires both recordType and recordId.");
|
|
83
|
+
if (![
|
|
84
|
+
"comment",
|
|
85
|
+
"react",
|
|
86
|
+
"vote",
|
|
87
|
+
"report"
|
|
88
|
+
].includes(this.action) || ![
|
|
89
|
+
"open",
|
|
90
|
+
"pre_moderated",
|
|
91
|
+
"members_only",
|
|
92
|
+
"closed"
|
|
93
|
+
].includes(this.moderationMode) || !Number.isSafeInteger(this.maxActions) || this.maxActions < 0 || !Number.isSafeInteger(this.windowSeconds) || this.windowSeconds < 0 || this.maxActions > 0 && this.windowSeconds === 0) throw new TypeError("Invalid participation policy.");
|
|
94
|
+
await super.validateBeforeSave();
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
__publicField$1(ParticipationPolicy, "__smrtPackage__", "@happyvertical/smrt-comments");
|
|
98
|
+
__decorateClass$1([tenantId()], ParticipationPolicy.prototype, "tenantId", 2);
|
|
99
|
+
__decorateClass$1([field()], ParticipationPolicy.prototype, "recordType", 2);
|
|
100
|
+
__decorateClass$1([field()], ParticipationPolicy.prototype, "recordId", 2);
|
|
101
|
+
__decorateClass$1([field({ required: true })], ParticipationPolicy.prototype, "action", 2);
|
|
102
|
+
__decorateClass$1([field({ required: true })], ParticipationPolicy.prototype, "moderationMode", 2);
|
|
103
|
+
__decorateClass$1([field({ required: true })], ParticipationPolicy.prototype, "maxActions", 2);
|
|
104
|
+
__decorateClass$1([field({ required: true })], ParticipationPolicy.prototype, "windowSeconds", 2);
|
|
105
|
+
ParticipationPolicy = __decorateClass$1([TenantScoped({ mode: "required" }), smrt({
|
|
106
|
+
tableName: "participation_policies",
|
|
107
|
+
api: false,
|
|
108
|
+
mcp: false,
|
|
109
|
+
cli: false
|
|
110
|
+
})], ParticipationPolicy);
|
|
111
|
+
//#endregion
|
|
112
|
+
//#region src/models/ParticipationRateBucket.ts
|
|
113
|
+
var __defProp = Object.defineProperty;
|
|
114
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
115
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, {
|
|
116
|
+
enumerable: true,
|
|
117
|
+
configurable: true,
|
|
118
|
+
writable: true,
|
|
119
|
+
value
|
|
120
|
+
}) : obj[key] = value;
|
|
121
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
122
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
123
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
124
|
+
if (kind && result) __defProp(target, key, result);
|
|
125
|
+
return result;
|
|
126
|
+
};
|
|
127
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
128
|
+
var ParticipationRateBucket = class extends SmrtObject {
|
|
129
|
+
tenantId = "";
|
|
130
|
+
bucketKey = "";
|
|
131
|
+
actionCount = 0;
|
|
132
|
+
};
|
|
133
|
+
__publicField(ParticipationRateBucket, "__smrtPackage__", "@happyvertical/smrt-comments");
|
|
134
|
+
__decorateClass([tenantId()], ParticipationRateBucket.prototype, "tenantId", 2);
|
|
135
|
+
__decorateClass([field({ required: true })], ParticipationRateBucket.prototype, "bucketKey", 2);
|
|
136
|
+
__decorateClass([field({ required: true })], ParticipationRateBucket.prototype, "actionCount", 2);
|
|
137
|
+
ParticipationRateBucket = __decorateClass([TenantScoped({ mode: "required" }), smrt({
|
|
138
|
+
tableName: "participation_rate_buckets",
|
|
139
|
+
api: false,
|
|
140
|
+
mcp: false,
|
|
141
|
+
cli: false,
|
|
142
|
+
indexes: [{
|
|
143
|
+
name: "participation_rate_bucket_unique",
|
|
144
|
+
columns: ["tenantId", "bucketKey"],
|
|
145
|
+
unique: true
|
|
146
|
+
}]
|
|
147
|
+
})], ParticipationRateBucket);
|
|
148
|
+
//#endregion
|
|
149
|
+
//#region src/collections/ParticipationCollection.ts
|
|
150
|
+
var ParticipationPolicyCollection = class extends SmrtCollection {
|
|
151
|
+
static _itemClass = ParticipationPolicy;
|
|
152
|
+
};
|
|
153
|
+
var ParticipationBanCollection = class extends SmrtCollection {
|
|
154
|
+
static _itemClass = ParticipationBan;
|
|
155
|
+
};
|
|
156
|
+
var ParticipationRateBucketCollection = class extends SmrtCollection {
|
|
157
|
+
static _itemClass = ParticipationRateBucket;
|
|
158
|
+
};
|
|
159
|
+
//#endregion
|
|
160
|
+
//#region src/participation-contract.ts
|
|
161
|
+
var ParticipationDeniedError = class extends Error {
|
|
162
|
+
constructor(reason) {
|
|
163
|
+
super(reason ? `Participation denied: ${reason}` : "Participation denied.");
|
|
164
|
+
this.reason = reason;
|
|
165
|
+
this.name = "ParticipationDeniedError";
|
|
166
|
+
}
|
|
167
|
+
reason;
|
|
168
|
+
};
|
|
169
|
+
//#endregion
|
|
170
|
+
//#region src/services/ParticipationService.ts
|
|
171
|
+
var canonical = (value) => /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value) ? value.toLowerCase() : value;
|
|
172
|
+
var ParticipationService = class {
|
|
173
|
+
constructor(options) {
|
|
174
|
+
this.options = options;
|
|
175
|
+
}
|
|
176
|
+
options;
|
|
177
|
+
normalize(request) {
|
|
178
|
+
if (!request || ![
|
|
179
|
+
"comment",
|
|
180
|
+
"react",
|
|
181
|
+
"vote",
|
|
182
|
+
"report"
|
|
183
|
+
].includes(request.action) || [
|
|
184
|
+
request.tenantId,
|
|
185
|
+
request.actorUserId,
|
|
186
|
+
request.recordType,
|
|
187
|
+
request.recordId
|
|
188
|
+
].some((value) => typeof value !== "string" || !value.trim())) throw new TypeError("Invalid participation request.");
|
|
189
|
+
return {
|
|
190
|
+
...request,
|
|
191
|
+
tenantId: canonical(request.tenantId),
|
|
192
|
+
actorUserId: canonical(request.actorUserId),
|
|
193
|
+
recordId: canonical(request.recordId)
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
async evaluate(request, db) {
|
|
197
|
+
const now = (this.options.now?.() ?? /* @__PURE__ */ new Date()).getTime();
|
|
198
|
+
if (!Number.isFinite(now)) throw new TypeError("Invalid participation clock.");
|
|
199
|
+
const rows = await (await ParticipationPolicyCollection.create({ db })).list({ where: {
|
|
200
|
+
tenantId: request.tenantId,
|
|
201
|
+
action: request.action
|
|
202
|
+
} });
|
|
203
|
+
const exact = rows.filter((p) => p.recordType === request.recordType && p.recordId === request.recordId);
|
|
204
|
+
const defaults = rows.filter((p) => p.recordType === "" && p.recordId === "");
|
|
205
|
+
if (exact.length > 1 || defaults.length > 1) throw new ParticipationDeniedError("Ambiguous participation policy.");
|
|
206
|
+
const policy = exact[0] ?? defaults[0];
|
|
207
|
+
if (policy && (![
|
|
208
|
+
"open",
|
|
209
|
+
"pre_moderated",
|
|
210
|
+
"members_only",
|
|
211
|
+
"closed"
|
|
212
|
+
].includes(policy.moderationMode) || !Number.isSafeInteger(policy.maxActions) || policy.maxActions < 0 || !Number.isSafeInteger(policy.windowSeconds) || policy.windowSeconds < 0 || policy.maxActions > 0 && policy.windowSeconds < 1)) throw new ParticipationDeniedError("Invalid participation policy.");
|
|
213
|
+
if (policy?.moderationMode === "closed") throw new ParticipationDeniedError("Participation is closed.");
|
|
214
|
+
const bans = await ParticipationBanCollection.create({ db });
|
|
215
|
+
for (const ban of await bans.list({ where: {
|
|
216
|
+
tenantId: request.tenantId,
|
|
217
|
+
actorUserId: request.actorUserId
|
|
218
|
+
} })) if (ban.recordType === "" && ban.recordId === "" || ban.recordType === request.recordType && ban.recordId === request.recordId) {
|
|
219
|
+
const expiry = ban.expiresAt === null ? null : new Date(ban.expiresAt).getTime();
|
|
220
|
+
if (expiry === null || !Number.isFinite(expiry) || expiry > now) throw new ParticipationDeniedError("Actor is banned.");
|
|
221
|
+
}
|
|
222
|
+
const decision = this.options.entitlement ? await this.options.entitlement.authorize(request) : { allowed: true };
|
|
223
|
+
if (decision?.allowed !== true || policy?.moderationMode === "members_only" && decision.member !== true) throw new ParticipationDeniedError(decision?.reason ?? "Membership required.");
|
|
224
|
+
if (decision.initialCommentStatus !== void 0 && !["visible", "pending"].includes(decision.initialCommentStatus)) throw new ParticipationDeniedError("Invalid moderation decision.");
|
|
225
|
+
return {
|
|
226
|
+
policy,
|
|
227
|
+
now,
|
|
228
|
+
decision: {
|
|
229
|
+
...decision,
|
|
230
|
+
initialCommentStatus: policy?.moderationMode === "pre_moderated" ? "pending" : decision.initialCommentStatus
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
/** Checks current permission without consuming quota (including keyed replays). */
|
|
235
|
+
async authorize(input) {
|
|
236
|
+
const request = this.normalize(input);
|
|
237
|
+
return withTenant({ tenantId: request.tenantId }, async () => (await this.evaluate(request, this.options.db)).decision);
|
|
238
|
+
}
|
|
239
|
+
async reserve(db, request, policy, now) {
|
|
240
|
+
if (!policy?.maxActions) return;
|
|
241
|
+
const buckets = await ParticipationRateBucketCollection.create({ db });
|
|
242
|
+
const bucketKey = digestRunOnceContent({
|
|
243
|
+
actor: request.actorUserId,
|
|
244
|
+
action: request.action,
|
|
245
|
+
recordType: policy.recordType,
|
|
246
|
+
recordId: policy.recordId,
|
|
247
|
+
windowSeconds: policy.windowSeconds,
|
|
248
|
+
window: Math.floor(now / (policy.windowSeconds * 1e3))
|
|
249
|
+
});
|
|
250
|
+
const table = `"${buckets.tableName.replace(/"/g, "\"\"")}"`;
|
|
251
|
+
const id = crypto.randomUUID();
|
|
252
|
+
if ((await db.query(`INSERT INTO ${table} (id, slug, context, tenant_id, bucket_key, action_count) VALUES (?, ?, '', ?, ?, 1) ON CONFLICT (tenant_id, bucket_key) DO UPDATE SET action_count = ${table}.action_count + 1 WHERE ${table}.action_count < ? RETURNING action_count`, id, id, request.tenantId, bucketKey, policy.maxActions)).rows.length !== 1) throw new ParticipationDeniedError("Rate limit exceeded.");
|
|
253
|
+
}
|
|
254
|
+
/** Run one new mutation atomically with its reservation. Caller owns retry identity. */
|
|
255
|
+
async execute(input, work) {
|
|
256
|
+
const request = this.normalize(input);
|
|
257
|
+
return withTenant({ tenantId: request.tenantId }, async () => {
|
|
258
|
+
const collection = await ParticipationRateBucketCollection.create({ db: this.options.db });
|
|
259
|
+
if (!collection.db.transaction) throw new Error("Participation requires transactions.");
|
|
260
|
+
return collection.db.transaction((tx) => this.executeInTransaction(tx, request, work));
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
/** Compose with an existing caller-owned transaction; propagate failures to roll it back. */
|
|
264
|
+
async executeInTransaction(tx, input, work) {
|
|
265
|
+
const request = this.normalize(input);
|
|
266
|
+
return withTenant({ tenantId: request.tenantId }, async () => {
|
|
267
|
+
const { policy, now, decision } = await this.evaluate(request, tx);
|
|
268
|
+
await this.reserve(tx, request, policy, now);
|
|
269
|
+
return work(tx, decision);
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
//#endregion
|
|
274
|
+
export { ParticipationRateBucketCollection as a, ParticipationBan as c, ParticipationPolicyCollection as i, ParticipationDeniedError as n, ParticipationRateBucket as o, ParticipationBanCollection as r, ParticipationPolicy as s, ParticipationService as t };
|
|
275
|
+
|
|
276
|
+
//# sourceMappingURL=participation-CLI-gl0-.js.map
|