@absolutejs/sync-pack-comments 0.5.0 → 0.6.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.
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +2 -2
- package/package.json +6 -3
package/dist/index.d.ts
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
* }));
|
|
24
24
|
* ```
|
|
25
25
|
*/
|
|
26
|
-
import { type CollectionContext, type CrdtMergeable, type SyncPack, type TableReader, type TableWriter } from
|
|
26
|
+
import { type CollectionContext, type CrdtMergeable, type SyncPack, type TableReader, type TableWriter } from '@absolutejs/sync/engine';
|
|
27
27
|
/** A comment row stored in the pack's owned table. */
|
|
28
28
|
export type CommentRow = {
|
|
29
29
|
id: string;
|
package/dist/index.js.map
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"/**\n * `@absolutejs/sync-pack-comments` — threaded comments on host-side resources\n * as a sync pack.\n *\n * Owns a single `comments` table. Per-resource read access is gated through\n * host-injected `canReadResource(resourceId, ctx)`. Edits are author-only;\n * deletes are author or moderator. Optional `bodyCrdt` config wires the\n * body field through `registerCrdt` for conflict-free editing. Optional\n * `joinUsers` config registers a `comments-with-author` join collection\n * that pairs each comment with the host's user row.\n *\n * @example\n * ```ts\n * import { createSyncEngine } from '@absolutejs/sync/engine';\n * import { createCommentsPack } from '@absolutejs/sync-pack-comments';\n *\n * const engine = createSyncEngine();\n * engine.registerPack(createCommentsPack({\n * getActorId: (ctx) => ctx.session.userId,\n * canReadResource: (resourceId, ctx) =>\n * hostAcl.canRead(resourceId, ctx.session.userId),\n * maxDepth: 8,\n * }));\n * ```\n */\n\nimport {\n createTextIndex,\n defineCollection,\n defineJoinCollection,\n defineMutation,\n defineSchema,\n defineSearchCollection,\n defineSyncPack,\n field,\n UnauthorizedError,\n type CollectionContext,\n type CrdtMergeable,\n type SyncPack,\n type TableReader,\n type TableWriter,\n} from \"@absolutejs/sync/engine\";\n\n/** A comment row stored in the pack's owned table. */\nexport type CommentRow = {\n id: string;\n resourceId: string;\n /** Null on top-level comments, the parent's id on replies. */\n parentCommentId: string | null;\n authorId: string;\n body: string;\n /** Depth in the thread (0 for top-level, parent.depth + 1 for replies). */\n depth: number;\n createdAt: number;\n /** Null until the comment is edited; otherwise the timestamp of the most\n * recent edit. */\n editedAt: number | null;\n};\n\n/** Storage adapter the pack uses for the comments table. */\nexport type CommentsStore = {\n reader: TableReader<CollectionContext>;\n writer: TableWriter<CommentRow, CollectionContext, unknown>;\n /** Lookup by row id; used by edit + delete mutations to verify ownership\n * and by create to walk the parent chain for depth math. */\n getById: (id: string) => CommentRow | undefined;\n};\n\n/** Default in-memory store. Each pack instance creates its own. */\nexport const createInMemoryCommentsStore = (): CommentsStore => {\n const rows = new Map<string, CommentRow>();\n return {\n getById: (id) => rows.get(id),\n reader: {\n all: () => [...rows.values()],\n },\n writer: {\n delete: (row) => {\n rows.delete((row as { id: string }).id);\n },\n insert: (data) => {\n rows.set(data.id, data);\n return data;\n },\n update: (data) => {\n const prior = rows.get(data.id);\n const merged = { ...(prior ?? {}), ...data } as CommentRow;\n rows.set(data.id, merged);\n return merged;\n },\n },\n };\n};\n\n/** Thrown when a reply would exceed the configured `maxDepth`. */\nexport class CommentDepthExceededError extends Error {\n readonly maxDepth: number;\n readonly attemptedDepth: number;\n constructor(maxDepth: number, attemptedDepth: number) {\n super(\n `Comment reply rejected: depth ${attemptedDepth} exceeds maxDepth ${maxDepth}`,\n );\n this.name = \"CommentDepthExceededError\";\n this.maxDepth = maxDepth;\n this.attemptedDepth = attemptedDepth;\n }\n}\n\n/** Thrown when the requested parent comment doesn't exist or doesn't match. */\nexport class CommentParentMismatchError extends Error {\n readonly parentCommentId: string;\n constructor(parentCommentId: string) {\n super(\n `Parent comment ${parentCommentId} not found, or its resourceId doesn't match the reply's resourceId`,\n );\n this.name = \"CommentParentMismatchError\";\n this.parentCommentId = parentCommentId;\n }\n}\n\n/** Factory config. `canReadResource` is the only required field. */\nexport type CommentsPackConfig<Ctx = CollectionContext, AuthorRow = unknown> = {\n /**\n * Prefix applied to the owned table name AND every collection/mutation\n * the pack exposes. Default `\"\"`. Use a non-empty prefix when\n * registering multiple comments packs on one engine (e.g. one per\n * product surface).\n */\n prefix?: string;\n /**\n * Extract the current actor id from the app's ctx. Default:\n * `(ctx) => ctx.userId`. **This is the only contract the pack assumes\n * about the app's ctx**, alongside `canReadResource` / `canModerate`.\n */\n getActorId?: (ctx: Ctx) => string | undefined;\n /**\n * Gate read access on a resource. The host knows which resources a\n * given ctx can read — the pack does NOT duplicate the host's ACL.\n * Required: pack reads return zero rows otherwise.\n */\n canReadResource: (resourceId: string, ctx: Ctx) => boolean;\n /**\n * Optional moderation predicate. Used by `comments:delete` — author OR\n * moderator can delete. Default `() => false` (only authors delete).\n */\n canModerate?: (ctx: Ctx) => boolean;\n /**\n * Maximum thread depth (top-level = 0, first reply = 1, ...). Replies\n * past this depth reject with {@link CommentDepthExceededError}.\n * Default `8`.\n */\n maxDepth?: number;\n /**\n * When provided, register the comment row's `body` field as a CRDT via\n * `engine.registerCrdt`. The engine then merges concurrent edits\n * instead of overwriting. Typically `yjsText` from\n * `@absolutejs/sync-yjs` — the pack does NOT import Yjs; the host\n * provides the mergeable.\n */\n bodyCrdt?: CrdtMergeable<unknown>;\n /**\n * Override the storage adapter. Default: a per-instance in-memory\n * store. Pass your own for a persistent backend (Drizzle, Postgres,\n * Redis, ...).\n */\n store?: CommentsStore;\n /** Wall-clock function. Default `Date.now`. */\n now?: () => number;\n /**\n * Generate a fresh row id. Default `crypto.randomUUID()`. Override for\n * deterministic ids in tests, or to route through your existing id\n * scheme.\n */\n newId?: () => string;\n /**\n * When set, the pack additionally registers a `comments-with-author`\n * join collection. The host's user table must have a registered\n * reader/writer (so user-row changes propagate into the join). The\n * pack itself does NOT own the users table; it only `readsTables`\n * the configured name.\n */\n joinUsers?: JoinUsersConfig<Ctx, AuthorRow>;\n /**\n * When set, register a `comments-search` full-text search collection\n * over the body field. The subscription's `params` is the query string;\n * results are the ranked top-K, re-ranked live as comments are added,\n * edited, or removed. Per-row read permission (which routes through\n * `canReadResource`) still filters hits per caller.\n */\n search?: { topK?: number };\n /**\n * When set, register a `comment_reactions` table + matching collection\n * + react / unreact / toggleReaction mutations. Each row is one\n * actor's reaction with a single emoji on a single comment, deterministic\n * id `${commentId}:${actorId}:${emoji}` for idempotent toggling.\n */\n reactions?: ReactionsConfig;\n};\n\n/** Config for the optional reactions surface. */\nexport type ReactionsConfig = {\n /**\n * Allowed emojis. Setting this filters react mutations — anything\n * outside the list is rejected. Empty array (default) means any\n * single-character or grapheme string is accepted.\n */\n allowedEmojis?: string[];\n /**\n * Custom row id generator for reaction rows. Default\n * `(commentId, actorId, emoji) => \\`${commentId}:${actorId}:${emoji}\\``.\n */\n newId?: (commentId: string, actorId: string, emoji: string) => string;\n};\n\n/** A reaction row — one per (comment, actor, emoji). */\nexport type CommentReactionRow = {\n id: string;\n commentId: string;\n actorId: string;\n emoji: string;\n createdAt: number;\n};\n\n/** Thrown when a react mutation supplies an emoji outside `allowedEmojis`. */\nexport class CommentReactionNotAllowedError extends Error {\n readonly emoji: string;\n constructor(emoji: string) {\n super(`Comment reaction \"${emoji}\" is not in the configured allowedEmojis`);\n this.name = \"CommentReactionNotAllowedError\";\n this.emoji = emoji;\n }\n}\n\n/**\n * Config for the optional `comments-with-author` join collection.\n *\n * The pack does not own the host's user table. The host supplies the\n * right-side hydrate (typically reading from its registered reader) and\n * the join key. The engine maintains the join incrementally — changes to\n * either side fan in through `engine.applyChange`.\n */\nexport type JoinUsersConfig<Ctx, AuthorRow = unknown> = {\n /**\n * The host's user table name. Surfaced in `readsTables` so the\n * dependency graph is reviewable. Default `\"users\"`.\n */\n table?: string;\n /**\n * Get the user's id (the join field). Default `(user) => user.id`.\n */\n key?: (user: AuthorRow) => string;\n /**\n * Host-side hydrate for the users side of the join. The pack passes\n * the subscription params + ctx; this returns the candidate users.\n * Returning all users (small set) or just the authors referenced by\n * the comments are both fine — the engine inner-joins on `authorId\n * === id` after hydrate.\n */\n hydrate: (\n params: { resourceId: string },\n ctx: Ctx,\n ) => Iterable<AuthorRow> | Promise<Iterable<AuthorRow>>;\n};\n\n/** A joined row emitted by the `comments-with-author` collection. */\nexport type CommentWithAuthor<AuthorRow = unknown> = CommentRow & {\n author: AuthorRow;\n};\n\nconst DEFAULT_MAX_DEPTH = 8;\nconst DEFAULT_USER_TABLE = \"users\";\n\nconst resolveActor = (\n getActorId: NonNullable<CommentsPackConfig[\"getActorId\"]>,\n ctx: unknown,\n): string => {\n const actorId = getActorId(ctx as CollectionContext);\n if (actorId === undefined || actorId === \"\") {\n throw new UnauthorizedError(\"comments mutation (no actor id)\");\n }\n return actorId;\n};\n\n/**\n * Build a {@link SyncPack} that exposes threaded comments. Each call returns\n * a fresh pack with its own store (unless a custom `store` is supplied).\n *\n * The `AuthorRow` generic is only consulted by the\n * `comments-with-author` join collection (when `joinUsers` is set); pass\n * it to recover types for the joined output. With no `joinUsers`, default\n * `unknown` is fine.\n */\nexport const createCommentsPack = <\n Ctx = CollectionContext,\n AuthorRow = unknown,\n>(\n config: CommentsPackConfig<Ctx, AuthorRow>,\n): SyncPack => {\n const prefix = config.prefix ?? \"\";\n const table = `${prefix}comments`;\n const collectionName = table;\n const createMutationName = `${prefix}comments:create`;\n const editMutationName = `${prefix}comments:edit`;\n const deleteMutationName = `${prefix}comments:delete`;\n const maxDepth = config.maxDepth ?? DEFAULT_MAX_DEPTH;\n const store = config.store ?? createInMemoryCommentsStore();\n const now = config.now ?? Date.now;\n const newId = config.newId ?? (() => globalThis.crypto.randomUUID());\n const getActorId = (config.getActorId ??\n ((ctx: CollectionContext) => (ctx as { userId?: string }).userId)) as (\n ctx: CollectionContext,\n ) => string | undefined;\n const canReadResource = config.canReadResource as (\n resourceId: string,\n ctx: CollectionContext,\n ) => boolean;\n const canModerate = (config.canModerate ?? (() => false)) as (\n ctx: CollectionContext,\n ) => boolean;\n\n type Params = { resourceId: string };\n\n const joinUsers = config.joinUsers;\n const userTable = joinUsers?.table ?? DEFAULT_USER_TABLE;\n const joinCollectionName = `${prefix}comments-with-author`;\n const userKey = (joinUsers?.key ??\n ((row: unknown) => (row as { id: string }).id)) as (\n user: unknown,\n ) => string;\n const search = config.search;\n const searchCollectionName = `${prefix}comments-search`;\n const searchTopK = search?.topK ?? 20;\n const reactionsConfig = config.reactions;\n const reactionsTable = `${prefix}comment_reactions`;\n const reactionsCollectionName = reactionsTable;\n const reactMutationName = `${prefix}comments:react`;\n const unreactMutationName = `${prefix}comments:unreact`;\n const toggleReactionMutationName = `${prefix}comments:toggleReaction`;\n const reactionId =\n reactionsConfig?.newId ??\n ((commentId: string, actorId: string, emoji: string) =>\n `${commentId}:${actorId}:${emoji}`);\n const reactionStore = new Map<string, CommentReactionRow>();\n const reactionReader: TableReader<CollectionContext> = {\n all: () => [...reactionStore.values()],\n };\n const reactionWriter: TableWriter<\n CommentReactionRow,\n CollectionContext,\n unknown\n > = {\n delete: (row) => {\n reactionStore.delete((row as { id: string }).id);\n },\n insert: (data) => {\n reactionStore.set(data.id, data);\n return data;\n },\n update: (data) => {\n reactionStore.set(data.id, data);\n return data;\n },\n };\n\n const pack: SyncPack = {\n name: \"@absolutejs/sync-pack-comments\",\n ownsTables:\n reactionsConfig === undefined ? [table] : [table, reactionsTable],\n readsTables: joinUsers === undefined ? [] : [userTable],\n version: \"0.4.0\",\n\n schemas: defineSchema({\n [table]: {\n fields: {\n id: field.string,\n resourceId: field.string,\n parentCommentId: (value) =>\n value === null || typeof value === \"string\",\n authorId: field.string,\n body: field.string,\n depth: field.number,\n createdAt: field.number,\n editedAt: (value) => value === null || typeof value === \"number\",\n },\n },\n }),\n\n readers: { [table]: store.reader },\n writers: { [table]: store.writer },\n\n permissions: {\n [table]: {\n read: (ctx: unknown, row: CommentRow) =>\n canReadResource(row.resourceId, ctx as CollectionContext),\n // The pack's own create handler stamps `authorId` from the\n // resolved actor, so this check passes inside the pack.\n // A host-side `actions.insert(\"comments\", { ... })` that\n // tried to forge an `authorId` would be rejected here.\n insert: (ctx: unknown, row: CommentRow) => {\n const callerId = getActorId(ctx as CollectionContext);\n return callerId !== undefined && row.authorId === callerId;\n },\n // Edits: author only. Same row-level check as insert.\n update: (ctx: unknown, row: CommentRow) => {\n const callerId = getActorId(ctx as CollectionContext);\n return callerId !== undefined && row.authorId === callerId;\n },\n // Deletes: author OR moderator. `actions.delete` may pass\n // the full row or just the key; if `authorId` isn't in the\n // supplied subject we look it up from the store.\n delete: (ctx: unknown, row: CommentRow) => {\n const callerId = getActorId(ctx as CollectionContext);\n if (callerId === undefined) return false;\n const authorId =\n (row as { authorId?: string }).authorId ??\n (() => {\n const id = (row as { id?: string }).id;\n return id === undefined ? undefined : store.getById(id)?.authorId;\n })();\n if (authorId === undefined) return false;\n if (authorId === callerId) return true;\n return canModerate(ctx as CollectionContext);\n },\n },\n },\n\n collections: [\n defineCollection<CommentRow, Params, CollectionContext>({\n name: collectionName,\n tables: [table],\n key: (row) => row.id,\n hydrate: (params, ctx) =>\n (store.reader.all(ctx) as CommentRow[]).filter(\n (row) =>\n row.resourceId === params.resourceId &&\n canReadResource(row.resourceId, ctx),\n ),\n match: (row, params, ctx) =>\n row.resourceId === params.resourceId &&\n canReadResource(row.resourceId, ctx),\n authorize: (params, ctx) => canReadResource(params.resourceId, ctx),\n }),\n ],\n\n mutations: [\n defineMutation<\n {\n resourceId: string;\n body: string;\n parentCommentId?: string | null;\n },\n CollectionContext,\n CommentRow\n >({\n name: createMutationName,\n handler: async (args, ctx, actions) => {\n if (!canReadResource(args.resourceId, ctx)) {\n throw new UnauthorizedError(\n `comments:create on resource \"${args.resourceId}\"`,\n );\n }\n const actorId = resolveActor(getActorId, ctx);\n let depth = 0;\n if (\n args.parentCommentId !== undefined &&\n args.parentCommentId !== null\n ) {\n const parent = store.getById(args.parentCommentId);\n if (parent === undefined || parent.resourceId !== args.resourceId) {\n throw new CommentParentMismatchError(args.parentCommentId);\n }\n depth = parent.depth + 1;\n if (depth > maxDepth) {\n throw new CommentDepthExceededError(maxDepth, depth);\n }\n }\n const row: CommentRow = {\n id: newId(),\n resourceId: args.resourceId,\n parentCommentId: args.parentCommentId ?? null,\n authorId: actorId,\n body: args.body,\n depth,\n createdAt: now(),\n editedAt: null,\n };\n return (await actions.insert(table, row)) as CommentRow;\n },\n }),\n defineMutation<\n { commentId: string; body: string },\n CollectionContext,\n CommentRow\n >({\n name: editMutationName,\n handler: async (args, ctx, actions) => {\n const actorId = resolveActor(getActorId, ctx);\n const existing = store.getById(args.commentId);\n if (existing === undefined) {\n throw new UnauthorizedError(\n `comments:edit on missing comment \"${args.commentId}\"`,\n );\n }\n if (existing.authorId !== actorId) {\n throw new UnauthorizedError(\n `comments:edit on \"${args.commentId}\" (not author)`,\n );\n }\n const updated: CommentRow = {\n ...existing,\n body: args.body,\n editedAt: now(),\n };\n return (await actions.update(table, updated)) as CommentRow;\n },\n }),\n defineMutation<{ commentId: string }, CollectionContext, void>({\n name: deleteMutationName,\n handler: async (args, ctx, actions) => {\n const actorId = resolveActor(getActorId, ctx);\n const existing = store.getById(args.commentId);\n if (existing === undefined) {\n throw new UnauthorizedError(\n `comments:delete on missing comment \"${args.commentId}\"`,\n );\n }\n const isAuthor = existing.authorId === actorId;\n const isModerator = canModerate(ctx);\n if (!isAuthor && !isModerator) {\n throw new UnauthorizedError(\n `comments:delete on \"${args.commentId}\" (not author, not moderator)`,\n );\n }\n await actions.delete(table, { id: args.commentId });\n },\n }),\n ],\n };\n\n if (config.bodyCrdt !== undefined) {\n pack.crdt = { [table]: { body: config.bodyCrdt } };\n }\n\n if (search !== undefined) {\n pack.searchCollections = [\n defineSearchCollection<CommentRow>({\n name: searchCollectionName,\n table,\n index: () =>\n createTextIndex<CommentRow>({\n fields: [\"body\"],\n key: (row) => row.id,\n }),\n key: (row) => row.id,\n limit: searchTopK,\n source: () => store.reader.all({}) as Iterable<CommentRow>,\n }),\n ];\n }\n\n if (joinUsers !== undefined) {\n const usersHydrate = joinUsers.hydrate;\n pack.joinCollections = [\n defineJoinCollection<\n CommentRow,\n unknown,\n CommentWithAuthor,\n Params,\n CollectionContext\n >({\n name: joinCollectionName,\n key: (out) => out.id,\n left: {\n table,\n hydrate: (params, ctx) =>\n (store.reader.all(ctx) as CommentRow[]).filter(\n (row) =>\n row.resourceId === params.resourceId &&\n canReadResource(row.resourceId, ctx),\n ),\n key: (row) => row.id,\n on: (row) => row.authorId,\n // Per-resource read gate, also applied to incoming changes.\n match: (row, params, ctx) =>\n row.resourceId === params.resourceId &&\n canReadResource(row.resourceId, ctx),\n },\n right: {\n table: userTable,\n hydrate: (params, ctx) =>\n usersHydrate(params, ctx as Ctx) as Iterable<unknown>,\n key: (user) => userKey(user),\n on: (user) => userKey(user),\n },\n select: (comment, author) => ({ ...comment, author }),\n authorize: (params, ctx) => canReadResource(params.resourceId, ctx),\n }),\n ];\n }\n\n if (reactionsConfig !== undefined) {\n const allowed = reactionsConfig.allowedEmojis;\n const isAllowed = (emoji: string): boolean =>\n allowed === undefined || allowed.length === 0\n ? true\n : allowed.includes(emoji);\n\n pack.schemas = {\n ...(pack.schemas ?? {}),\n [reactionsTable]: {\n fields: {\n actorId: field.string,\n commentId: field.string,\n createdAt: field.number,\n emoji: field.string,\n id: field.string,\n },\n },\n };\n pack.readers = {\n ...(pack.readers ?? {}),\n [reactionsTable]: reactionReader,\n };\n pack.writers = {\n ...(pack.writers ?? {}),\n [reactionsTable]: reactionWriter,\n };\n pack.permissions = {\n ...(pack.permissions ?? {}),\n [reactionsTable]: {\n // Reactions are visible to anyone who can read the comment's\n // resource. We look the comment up to gate on canReadResource.\n read: (ctx: unknown, row: CommentReactionRow) => {\n const comment = store.getById(row.commentId);\n if (comment === undefined) return false;\n return canReadResource(comment.resourceId, ctx as CollectionContext);\n },\n // Per-row writes: the row's actorId must match the caller.\n insert: (ctx: unknown, row: CommentReactionRow) => {\n const callerId = getActorId(ctx as CollectionContext);\n return callerId !== undefined && row.actorId === callerId;\n },\n update: (ctx: unknown, row: CommentReactionRow) => {\n const callerId = getActorId(ctx as CollectionContext);\n return callerId !== undefined && row.actorId === callerId;\n },\n delete: (ctx: unknown, row: CommentReactionRow) => {\n const callerId = getActorId(ctx as CollectionContext);\n if (callerId === undefined) return false;\n if ((row as { actorId?: string }).actorId !== undefined) {\n return (row as { actorId: string }).actorId === callerId;\n }\n const id = (row as { id?: string }).id;\n if (id === undefined) return false;\n return reactionStore.get(id)?.actorId === callerId;\n },\n },\n };\n\n pack.collections = [\n ...(pack.collections ?? []),\n defineCollection<\n CommentReactionRow,\n { commentId: string },\n CollectionContext\n >({\n name: reactionsCollectionName,\n tables: [reactionsTable],\n key: (row) => row.id,\n hydrate: (params, ctx) => {\n const comment = store.getById(params.commentId);\n if (\n comment === undefined ||\n !canReadResource(comment.resourceId, ctx)\n ) {\n return [];\n }\n return (reactionReader.all(ctx) as CommentReactionRow[]).filter(\n (row) => row.commentId === params.commentId,\n );\n },\n match: (row, params, ctx) => {\n if (row.commentId !== params.commentId) return false;\n const comment = store.getById(params.commentId);\n return (\n comment !== undefined && canReadResource(comment.resourceId, ctx)\n );\n },\n authorize: (params, ctx) => {\n const comment = store.getById(params.commentId);\n return (\n comment !== undefined && canReadResource(comment.resourceId, ctx)\n );\n },\n }),\n ];\n\n pack.mutations = [\n ...(pack.mutations ?? []),\n defineMutation<\n { commentId: string; emoji: string },\n CollectionContext,\n CommentReactionRow\n >({\n name: reactMutationName,\n handler: async (args, ctx, actions) => {\n if (!isAllowed(args.emoji)) {\n throw new CommentReactionNotAllowedError(args.emoji);\n }\n const actorId = resolveActor(getActorId, ctx);\n const comment = store.getById(args.commentId);\n if (comment === undefined) {\n throw new UnauthorizedError(\n `comments:react on missing comment \"${args.commentId}\"`,\n );\n }\n if (!canReadResource(comment.resourceId, ctx)) {\n throw new UnauthorizedError(\n `comments:react on comment \"${args.commentId}\" (no read access)`,\n );\n }\n const id = reactionId(args.commentId, actorId, args.emoji);\n const existing = reactionStore.get(id);\n if (existing !== undefined) return existing;\n const row: CommentReactionRow = {\n actorId,\n commentId: args.commentId,\n createdAt: now(),\n emoji: args.emoji,\n id,\n };\n return (await actions.insert(\n reactionsTable,\n row,\n )) as CommentReactionRow;\n },\n }),\n defineMutation<\n { commentId: string; emoji: string },\n CollectionContext,\n void\n >({\n name: unreactMutationName,\n handler: async (args, ctx, actions) => {\n const actorId = resolveActor(getActorId, ctx);\n const id = reactionId(args.commentId, actorId, args.emoji);\n if (reactionStore.get(id) === undefined) return;\n await actions.delete(reactionsTable, { id });\n },\n }),\n defineMutation<\n { commentId: string; emoji: string },\n CollectionContext,\n { reacted: boolean }\n >({\n name: toggleReactionMutationName,\n handler: async (args, ctx, actions) => {\n if (!isAllowed(args.emoji)) {\n throw new CommentReactionNotAllowedError(args.emoji);\n }\n const actorId = resolveActor(getActorId, ctx);\n const comment = store.getById(args.commentId);\n if (comment === undefined) {\n throw new UnauthorizedError(\n `comments:toggleReaction on missing comment \"${args.commentId}\"`,\n );\n }\n if (!canReadResource(comment.resourceId, ctx)) {\n throw new UnauthorizedError(\n `comments:toggleReaction on comment \"${args.commentId}\" (no read access)`,\n );\n }\n const id = reactionId(args.commentId, actorId, args.emoji);\n const existing = reactionStore.get(id);\n if (existing === undefined) {\n const row: CommentReactionRow = {\n actorId,\n commentId: args.commentId,\n createdAt: now(),\n emoji: args.emoji,\n id,\n };\n await actions.insert(reactionsTable, row);\n return { reacted: true };\n }\n await actions.delete(reactionsTable, { id });\n return { reacted: false };\n },\n }),\n ];\n }\n\n return defineSyncPack(pack);\n};\n"
|
|
5
|
+
"/**\n * `@absolutejs/sync-pack-comments` — threaded comments on host-side resources\n * as a sync pack.\n *\n * Owns a single `comments` table. Per-resource read access is gated through\n * host-injected `canReadResource(resourceId, ctx)`. Edits are author-only;\n * deletes are author or moderator. Optional `bodyCrdt` config wires the\n * body field through `registerCrdt` for conflict-free editing. Optional\n * `joinUsers` config registers a `comments-with-author` join collection\n * that pairs each comment with the host's user row.\n *\n * @example\n * ```ts\n * import { createSyncEngine } from '@absolutejs/sync/engine';\n * import { createCommentsPack } from '@absolutejs/sync-pack-comments';\n *\n * const engine = createSyncEngine();\n * engine.registerPack(createCommentsPack({\n * getActorId: (ctx) => ctx.session.userId,\n * canReadResource: (resourceId, ctx) =>\n * hostAcl.canRead(resourceId, ctx.session.userId),\n * maxDepth: 8,\n * }));\n * ```\n */\n\nimport {\n\tcreateTextIndex,\n\tdefineCollection,\n\tdefineJoinCollection,\n\tdefineMutation,\n\tdefineSchema,\n\tdefineSearchCollection,\n\tdefineSyncPack,\n\tfield,\n\tUnauthorizedError,\n\ttype CollectionContext,\n\ttype CrdtMergeable,\n\ttype SyncPack,\n\ttype TableReader,\n\ttype TableWriter\n} from '@absolutejs/sync/engine';\n\n/** A comment row stored in the pack's owned table. */\nexport type CommentRow = {\n\tid: string;\n\tresourceId: string;\n\t/** Null on top-level comments, the parent's id on replies. */\n\tparentCommentId: string | null;\n\tauthorId: string;\n\tbody: string;\n\t/** Depth in the thread (0 for top-level, parent.depth + 1 for replies). */\n\tdepth: number;\n\tcreatedAt: number;\n\t/** Null until the comment is edited; otherwise the timestamp of the most\n\t * recent edit. */\n\teditedAt: number | null;\n};\n\n/** Storage adapter the pack uses for the comments table. */\nexport type CommentsStore = {\n\treader: TableReader<CollectionContext>;\n\twriter: TableWriter<CommentRow, CollectionContext, unknown>;\n\t/** Lookup by row id; used by edit + delete mutations to verify ownership\n\t * and by create to walk the parent chain for depth math. */\n\tgetById: (id: string) => CommentRow | undefined;\n};\n\n/** Default in-memory store. Each pack instance creates its own. */\nexport const createInMemoryCommentsStore = (): CommentsStore => {\n\tconst rows = new Map<string, CommentRow>();\n\treturn {\n\t\tgetById: (id) => rows.get(id),\n\t\treader: {\n\t\t\tall: () => [...rows.values()]\n\t\t},\n\t\twriter: {\n\t\t\tdelete: (row) => {\n\t\t\t\trows.delete((row as { id: string }).id);\n\t\t\t},\n\t\t\tinsert: (data) => {\n\t\t\t\trows.set(data.id, data);\n\t\t\t\treturn data;\n\t\t\t},\n\t\t\tupdate: (data) => {\n\t\t\t\tconst prior = rows.get(data.id);\n\t\t\t\tconst merged = { ...(prior ?? {}), ...data } as CommentRow;\n\t\t\t\trows.set(data.id, merged);\n\t\t\t\treturn merged;\n\t\t\t}\n\t\t}\n\t};\n};\n\n/** Thrown when a reply would exceed the configured `maxDepth`. */\nexport class CommentDepthExceededError extends Error {\n\treadonly maxDepth: number;\n\treadonly attemptedDepth: number;\n\tconstructor(maxDepth: number, attemptedDepth: number) {\n\t\tsuper(\n\t\t\t`Comment reply rejected: depth ${attemptedDepth} exceeds maxDepth ${maxDepth}`\n\t\t);\n\t\tthis.name = 'CommentDepthExceededError';\n\t\tthis.maxDepth = maxDepth;\n\t\tthis.attemptedDepth = attemptedDepth;\n\t}\n}\n\n/** Thrown when the requested parent comment doesn't exist or doesn't match. */\nexport class CommentParentMismatchError extends Error {\n\treadonly parentCommentId: string;\n\tconstructor(parentCommentId: string) {\n\t\tsuper(\n\t\t\t`Parent comment ${parentCommentId} not found, or its resourceId doesn't match the reply's resourceId`\n\t\t);\n\t\tthis.name = 'CommentParentMismatchError';\n\t\tthis.parentCommentId = parentCommentId;\n\t}\n}\n\n/** Factory config. `canReadResource` is the only required field. */\nexport type CommentsPackConfig<\n\tCtx = CollectionContext,\n\tAuthorRow = unknown\n> = {\n\t/**\n\t * Prefix applied to the owned table name AND every collection/mutation\n\t * the pack exposes. Default `\"\"`. Use a non-empty prefix when\n\t * registering multiple comments packs on one engine (e.g. one per\n\t * product surface).\n\t */\n\tprefix?: string;\n\t/**\n\t * Extract the current actor id from the app's ctx. Default:\n\t * `(ctx) => ctx.userId`. **This is the only contract the pack assumes\n\t * about the app's ctx**, alongside `canReadResource` / `canModerate`.\n\t */\n\tgetActorId?: (ctx: Ctx) => string | undefined;\n\t/**\n\t * Gate read access on a resource. The host knows which resources a\n\t * given ctx can read — the pack does NOT duplicate the host's ACL.\n\t * Required: pack reads return zero rows otherwise.\n\t */\n\tcanReadResource: (resourceId: string, ctx: Ctx) => boolean;\n\t/**\n\t * Optional moderation predicate. Used by `comments:delete` — author OR\n\t * moderator can delete. Default `() => false` (only authors delete).\n\t */\n\tcanModerate?: (ctx: Ctx) => boolean;\n\t/**\n\t * Maximum thread depth (top-level = 0, first reply = 1, ...). Replies\n\t * past this depth reject with {@link CommentDepthExceededError}.\n\t * Default `8`.\n\t */\n\tmaxDepth?: number;\n\t/**\n\t * When provided, register the comment row's `body` field as a CRDT via\n\t * `engine.registerCrdt`. The engine then merges concurrent edits\n\t * instead of overwriting. Typically `yjsText` from\n\t * `@absolutejs/sync-yjs` — the pack does NOT import Yjs; the host\n\t * provides the mergeable.\n\t */\n\tbodyCrdt?: CrdtMergeable<unknown>;\n\t/**\n\t * Override the storage adapter. Default: a per-instance in-memory\n\t * store. Pass your own for a persistent backend (Drizzle, Postgres,\n\t * Redis, ...).\n\t */\n\tstore?: CommentsStore;\n\t/** Wall-clock function. Default `Date.now`. */\n\tnow?: () => number;\n\t/**\n\t * Generate a fresh row id. Default `crypto.randomUUID()`. Override for\n\t * deterministic ids in tests, or to route through your existing id\n\t * scheme.\n\t */\n\tnewId?: () => string;\n\t/**\n\t * When set, the pack additionally registers a `comments-with-author`\n\t * join collection. The host's user table must have a registered\n\t * reader/writer (so user-row changes propagate into the join). The\n\t * pack itself does NOT own the users table; it only `readsTables`\n\t * the configured name.\n\t */\n\tjoinUsers?: JoinUsersConfig<Ctx, AuthorRow>;\n\t/**\n\t * When set, register a `comments-search` full-text search collection\n\t * over the body field. The subscription's `params` is the query string;\n\t * results are the ranked top-K, re-ranked live as comments are added,\n\t * edited, or removed. Per-row read permission (which routes through\n\t * `canReadResource`) still filters hits per caller.\n\t */\n\tsearch?: { topK?: number };\n\t/**\n\t * When set, register a `comment_reactions` table + matching collection\n\t * + react / unreact / toggleReaction mutations. Each row is one\n\t * actor's reaction with a single emoji on a single comment, deterministic\n\t * id `${commentId}:${actorId}:${emoji}` for idempotent toggling.\n\t */\n\treactions?: ReactionsConfig;\n};\n\n/** Config for the optional reactions surface. */\nexport type ReactionsConfig = {\n\t/**\n\t * Allowed emojis. Setting this filters react mutations — anything\n\t * outside the list is rejected. Empty array (default) means any\n\t * single-character or grapheme string is accepted.\n\t */\n\tallowedEmojis?: string[];\n\t/**\n\t * Custom row id generator for reaction rows. Default\n\t * `(commentId, actorId, emoji) => \\`${commentId}:${actorId}:${emoji}\\``.\n\t */\n\tnewId?: (\n\t\tcommentId: string,\n\t\tactorId: string,\n\t\temoji: string\n\t) => string;\n};\n\n/** A reaction row — one per (comment, actor, emoji). */\nexport type CommentReactionRow = {\n\tid: string;\n\tcommentId: string;\n\tactorId: string;\n\temoji: string;\n\tcreatedAt: number;\n};\n\n/** Thrown when a react mutation supplies an emoji outside `allowedEmojis`. */\nexport class CommentReactionNotAllowedError extends Error {\n\treadonly emoji: string;\n\tconstructor(emoji: string) {\n\t\tsuper(\n\t\t\t`Comment reaction \"${emoji}\" is not in the configured allowedEmojis`\n\t\t);\n\t\tthis.name = 'CommentReactionNotAllowedError';\n\t\tthis.emoji = emoji;\n\t}\n}\n\n/**\n * Config for the optional `comments-with-author` join collection.\n *\n * The pack does not own the host's user table. The host supplies the\n * right-side hydrate (typically reading from its registered reader) and\n * the join key. The engine maintains the join incrementally — changes to\n * either side fan in through `engine.applyChange`.\n */\nexport type JoinUsersConfig<Ctx, AuthorRow = unknown> = {\n\t/**\n\t * The host's user table name. Surfaced in `readsTables` so the\n\t * dependency graph is reviewable. Default `\"users\"`.\n\t */\n\ttable?: string;\n\t/**\n\t * Get the user's id (the join field). Default `(user) => user.id`.\n\t */\n\tkey?: (user: AuthorRow) => string;\n\t/**\n\t * Host-side hydrate for the users side of the join. The pack passes\n\t * the subscription params + ctx; this returns the candidate users.\n\t * Returning all users (small set) or just the authors referenced by\n\t * the comments are both fine — the engine inner-joins on `authorId\n\t * === id` after hydrate.\n\t */\n\thydrate: (\n\t\tparams: { resourceId: string },\n\t\tctx: Ctx\n\t) => Iterable<AuthorRow> | Promise<Iterable<AuthorRow>>;\n};\n\n/** A joined row emitted by the `comments-with-author` collection. */\nexport type CommentWithAuthor<AuthorRow = unknown> = CommentRow & {\n\tauthor: AuthorRow;\n};\n\nconst DEFAULT_MAX_DEPTH = 8;\nconst DEFAULT_USER_TABLE = 'users';\n\nconst resolveActor = (\n\tgetActorId: NonNullable<CommentsPackConfig['getActorId']>,\n\tctx: unknown\n): string => {\n\tconst actorId = getActorId(ctx as CollectionContext);\n\tif (actorId === undefined || actorId === '') {\n\t\tthrow new UnauthorizedError('comments mutation (no actor id)');\n\t}\n\treturn actorId;\n};\n\n/**\n * Build a {@link SyncPack} that exposes threaded comments. Each call returns\n * a fresh pack with its own store (unless a custom `store` is supplied).\n *\n * The `AuthorRow` generic is only consulted by the\n * `comments-with-author` join collection (when `joinUsers` is set); pass\n * it to recover types for the joined output. With no `joinUsers`, default\n * `unknown` is fine.\n */\nexport const createCommentsPack = <\n\tCtx = CollectionContext,\n\tAuthorRow = unknown\n>(\n\tconfig: CommentsPackConfig<Ctx, AuthorRow>\n): SyncPack => {\n\tconst prefix = config.prefix ?? '';\n\tconst table = `${prefix}comments`;\n\tconst collectionName = table;\n\tconst createMutationName = `${prefix}comments:create`;\n\tconst editMutationName = `${prefix}comments:edit`;\n\tconst deleteMutationName = `${prefix}comments:delete`;\n\tconst maxDepth = config.maxDepth ?? DEFAULT_MAX_DEPTH;\n\tconst store = config.store ?? createInMemoryCommentsStore();\n\tconst now = config.now ?? Date.now;\n\tconst newId =\n\t\tconfig.newId ?? (() => globalThis.crypto.randomUUID());\n\tconst getActorId = (config.getActorId ??\n\t\t((ctx: CollectionContext) =>\n\t\t\t(ctx as { userId?: string }).userId)) as (\n\t\tctx: CollectionContext\n\t) => string | undefined;\n\tconst canReadResource = config.canReadResource as (\n\t\tresourceId: string,\n\t\tctx: CollectionContext\n\t) => boolean;\n\tconst canModerate = (config.canModerate ?? (() => false)) as (\n\t\tctx: CollectionContext\n\t) => boolean;\n\n\ttype Params = { resourceId: string };\n\n\tconst joinUsers = config.joinUsers;\n\tconst userTable = joinUsers?.table ?? DEFAULT_USER_TABLE;\n\tconst joinCollectionName = `${prefix}comments-with-author`;\n\tconst userKey = (joinUsers?.key ?? ((row: unknown) =>\n\t\t(row as { id: string }).id)) as (user: unknown) => string;\n\tconst search = config.search;\n\tconst searchCollectionName = `${prefix}comments-search`;\n\tconst searchTopK = search?.topK ?? 20;\n\tconst reactionsConfig = config.reactions;\n\tconst reactionsTable = `${prefix}comment_reactions`;\n\tconst reactionsCollectionName = reactionsTable;\n\tconst reactMutationName = `${prefix}comments:react`;\n\tconst unreactMutationName = `${prefix}comments:unreact`;\n\tconst toggleReactionMutationName = `${prefix}comments:toggleReaction`;\n\tconst reactionId =\n\t\treactionsConfig?.newId ??\n\t\t((commentId: string, actorId: string, emoji: string) =>\n\t\t\t`${commentId}:${actorId}:${emoji}`);\n\tconst reactionStore = new Map<string, CommentReactionRow>();\n\tconst reactionReader: TableReader<CollectionContext> = {\n\t\tall: () => [...reactionStore.values()]\n\t};\n\tconst reactionWriter: TableWriter<\n\t\tCommentReactionRow,\n\t\tCollectionContext,\n\t\tunknown\n\t> = {\n\t\tdelete: (row) => {\n\t\t\treactionStore.delete((row as { id: string }).id);\n\t\t},\n\t\tinsert: (data) => {\n\t\t\treactionStore.set(data.id, data);\n\t\t\treturn data;\n\t\t},\n\t\tupdate: (data) => {\n\t\t\treactionStore.set(data.id, data);\n\t\t\treturn data;\n\t\t}\n\t};\n\n\tconst pack: SyncPack = {\n\t\tname: '@absolutejs/sync-pack-comments',\n\t\townsTables:\n\t\t\treactionsConfig === undefined ? [table] : [table, reactionsTable],\n\t\treadsTables: joinUsers === undefined ? [] : [userTable],\n\t\tversion: '0.4.0',\n\n\t\tschemas: defineSchema({\n\t\t\t[table]: {\n\t\t\t\tfields: {\n\t\t\t\t\tid: field.string,\n\t\t\t\t\tresourceId: field.string,\n\t\t\t\t\tparentCommentId: (value) =>\n\t\t\t\t\t\tvalue === null || typeof value === 'string',\n\t\t\t\t\tauthorId: field.string,\n\t\t\t\t\tbody: field.string,\n\t\t\t\t\tdepth: field.number,\n\t\t\t\t\tcreatedAt: field.number,\n\t\t\t\t\teditedAt: (value) =>\n\t\t\t\t\t\tvalue === null || typeof value === 'number'\n\t\t\t\t}\n\t\t\t}\n\t\t}),\n\n\t\treaders: { [table]: store.reader },\n\t\twriters: { [table]: store.writer },\n\n\t\tpermissions: {\n\t\t\t[table]: {\n\t\t\t\tread: (ctx: unknown, row: CommentRow) =>\n\t\t\t\t\tcanReadResource(row.resourceId, ctx as CollectionContext),\n\t\t\t\t// The pack's own create handler stamps `authorId` from the\n\t\t\t\t// resolved actor, so this check passes inside the pack.\n\t\t\t\t// A host-side `actions.insert(\"comments\", { ... })` that\n\t\t\t\t// tried to forge an `authorId` would be rejected here.\n\t\t\t\tinsert: (ctx: unknown, row: CommentRow) => {\n\t\t\t\t\tconst callerId = getActorId(ctx as CollectionContext);\n\t\t\t\t\treturn callerId !== undefined && row.authorId === callerId;\n\t\t\t\t},\n\t\t\t\t// Edits: author only. Same row-level check as insert.\n\t\t\t\tupdate: (ctx: unknown, row: CommentRow) => {\n\t\t\t\t\tconst callerId = getActorId(ctx as CollectionContext);\n\t\t\t\t\treturn callerId !== undefined && row.authorId === callerId;\n\t\t\t\t},\n\t\t\t\t// Deletes: author OR moderator. `actions.delete` may pass\n\t\t\t\t// the full row or just the key; if `authorId` isn't in the\n\t\t\t\t// supplied subject we look it up from the store.\n\t\t\t\tdelete: (ctx: unknown, row: CommentRow) => {\n\t\t\t\t\tconst callerId = getActorId(ctx as CollectionContext);\n\t\t\t\t\tif (callerId === undefined) return false;\n\t\t\t\t\tconst authorId =\n\t\t\t\t\t\t(row as { authorId?: string }).authorId ??\n\t\t\t\t\t\t(() => {\n\t\t\t\t\t\t\tconst id = (row as { id?: string }).id;\n\t\t\t\t\t\t\treturn id === undefined\n\t\t\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t\t\t: store.getById(id)?.authorId;\n\t\t\t\t\t\t})();\n\t\t\t\t\tif (authorId === undefined) return false;\n\t\t\t\t\tif (authorId === callerId) return true;\n\t\t\t\t\treturn canModerate(ctx as CollectionContext);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\tcollections: [\n\t\t\tdefineCollection<CommentRow, Params, CollectionContext>({\n\t\t\t\tname: collectionName,\n\t\t\t\ttables: [table],\n\t\t\t\tkey: (row) => row.id,\n\t\t\t\thydrate: (params, ctx) =>\n\t\t\t\t\t(store.reader.all(ctx) as CommentRow[]).filter(\n\t\t\t\t\t\t(row) =>\n\t\t\t\t\t\t\trow.resourceId === params.resourceId &&\n\t\t\t\t\t\t\tcanReadResource(row.resourceId, ctx)\n\t\t\t\t\t),\n\t\t\t\tmatch: (row, params, ctx) =>\n\t\t\t\t\trow.resourceId === params.resourceId &&\n\t\t\t\t\tcanReadResource(row.resourceId, ctx),\n\t\t\t\tauthorize: (params, ctx) =>\n\t\t\t\t\tcanReadResource(params.resourceId, ctx)\n\t\t\t})\n\t\t],\n\n\t\tmutations: [\n\t\t\tdefineMutation<\n\t\t\t\t{\n\t\t\t\t\tresourceId: string;\n\t\t\t\t\tbody: string;\n\t\t\t\t\tparentCommentId?: string | null;\n\t\t\t\t},\n\t\t\t\tCollectionContext,\n\t\t\t\tCommentRow\n\t\t\t>({\n\t\t\t\tname: createMutationName,\n\t\t\t\thandler: async (args, ctx, actions) => {\n\t\t\t\t\tif (!canReadResource(args.resourceId, ctx)) {\n\t\t\t\t\t\tthrow new UnauthorizedError(\n\t\t\t\t\t\t\t`comments:create on resource \"${args.resourceId}\"`\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tconst actorId = resolveActor(getActorId, ctx);\n\t\t\t\t\tlet depth = 0;\n\t\t\t\t\tif (\n\t\t\t\t\t\targs.parentCommentId !== undefined &&\n\t\t\t\t\t\targs.parentCommentId !== null\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst parent = store.getById(args.parentCommentId);\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tparent === undefined ||\n\t\t\t\t\t\t\tparent.resourceId !== args.resourceId\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tthrow new CommentParentMismatchError(\n\t\t\t\t\t\t\t\targs.parentCommentId\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tdepth = parent.depth + 1;\n\t\t\t\t\t\tif (depth > maxDepth) {\n\t\t\t\t\t\t\tthrow new CommentDepthExceededError(\n\t\t\t\t\t\t\t\tmaxDepth,\n\t\t\t\t\t\t\t\tdepth\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tconst row: CommentRow = {\n\t\t\t\t\t\tid: newId(),\n\t\t\t\t\t\tresourceId: args.resourceId,\n\t\t\t\t\t\tparentCommentId: args.parentCommentId ?? null,\n\t\t\t\t\t\tauthorId: actorId,\n\t\t\t\t\t\tbody: args.body,\n\t\t\t\t\t\tdepth,\n\t\t\t\t\t\tcreatedAt: now(),\n\t\t\t\t\t\teditedAt: null\n\t\t\t\t\t};\n\t\t\t\t\treturn (await actions.insert(table, row)) as CommentRow;\n\t\t\t\t}\n\t\t\t}),\n\t\t\tdefineMutation<\n\t\t\t\t{ commentId: string; body: string },\n\t\t\t\tCollectionContext,\n\t\t\t\tCommentRow\n\t\t\t>({\n\t\t\t\tname: editMutationName,\n\t\t\t\thandler: async (args, ctx, actions) => {\n\t\t\t\t\tconst actorId = resolveActor(getActorId, ctx);\n\t\t\t\t\tconst existing = store.getById(args.commentId);\n\t\t\t\t\tif (existing === undefined) {\n\t\t\t\t\t\tthrow new UnauthorizedError(\n\t\t\t\t\t\t\t`comments:edit on missing comment \"${args.commentId}\"`\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (existing.authorId !== actorId) {\n\t\t\t\t\t\tthrow new UnauthorizedError(\n\t\t\t\t\t\t\t`comments:edit on \"${args.commentId}\" (not author)`\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tconst updated: CommentRow = {\n\t\t\t\t\t\t...existing,\n\t\t\t\t\t\tbody: args.body,\n\t\t\t\t\t\teditedAt: now()\n\t\t\t\t\t};\n\t\t\t\t\treturn (await actions.update(table, updated)) as CommentRow;\n\t\t\t\t}\n\t\t\t}),\n\t\t\tdefineMutation<{ commentId: string }, CollectionContext, void>({\n\t\t\t\tname: deleteMutationName,\n\t\t\t\thandler: async (args, ctx, actions) => {\n\t\t\t\t\tconst actorId = resolveActor(getActorId, ctx);\n\t\t\t\t\tconst existing = store.getById(args.commentId);\n\t\t\t\t\tif (existing === undefined) {\n\t\t\t\t\t\tthrow new UnauthorizedError(\n\t\t\t\t\t\t\t`comments:delete on missing comment \"${args.commentId}\"`\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tconst isAuthor = existing.authorId === actorId;\n\t\t\t\t\tconst isModerator = canModerate(ctx);\n\t\t\t\t\tif (!isAuthor && !isModerator) {\n\t\t\t\t\t\tthrow new UnauthorizedError(\n\t\t\t\t\t\t\t`comments:delete on \"${args.commentId}\" (not author, not moderator)`\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tawait actions.delete(table, { id: args.commentId });\n\t\t\t\t}\n\t\t\t})\n\t\t]\n\t};\n\n\tif (config.bodyCrdt !== undefined) {\n\t\tpack.crdt = { [table]: { body: config.bodyCrdt } };\n\t}\n\n\tif (search !== undefined) {\n\t\tpack.searchCollections = [\n\t\t\tdefineSearchCollection<CommentRow>({\n\t\t\t\tname: searchCollectionName,\n\t\t\t\ttable,\n\t\t\t\tindex: () =>\n\t\t\t\t\tcreateTextIndex<CommentRow>({\n\t\t\t\t\t\tfields: ['body'],\n\t\t\t\t\t\tkey: (row) => row.id\n\t\t\t\t\t}),\n\t\t\t\tkey: (row) => row.id,\n\t\t\t\tlimit: searchTopK,\n\t\t\t\tsource: () =>\n\t\t\t\t\tstore.reader.all({}) as Iterable<CommentRow>\n\t\t\t})\n\t\t];\n\t}\n\n\tif (joinUsers !== undefined) {\n\t\tconst usersHydrate = joinUsers.hydrate;\n\t\tpack.joinCollections = [\n\t\t\tdefineJoinCollection<\n\t\t\t\tCommentRow,\n\t\t\t\tunknown,\n\t\t\t\tCommentWithAuthor,\n\t\t\t\tParams,\n\t\t\t\tCollectionContext\n\t\t\t>({\n\t\t\t\tname: joinCollectionName,\n\t\t\t\tkey: (out) => out.id,\n\t\t\t\tleft: {\n\t\t\t\t\ttable,\n\t\t\t\t\thydrate: (params, ctx) =>\n\t\t\t\t\t\t(store.reader.all(ctx) as CommentRow[]).filter(\n\t\t\t\t\t\t\t(row) =>\n\t\t\t\t\t\t\t\trow.resourceId === params.resourceId &&\n\t\t\t\t\t\t\t\tcanReadResource(row.resourceId, ctx)\n\t\t\t\t\t\t),\n\t\t\t\t\tkey: (row) => row.id,\n\t\t\t\t\ton: (row) => row.authorId,\n\t\t\t\t\t// Per-resource read gate, also applied to incoming changes.\n\t\t\t\t\tmatch: (row, params, ctx) =>\n\t\t\t\t\t\trow.resourceId === params.resourceId &&\n\t\t\t\t\t\tcanReadResource(row.resourceId, ctx)\n\t\t\t\t},\n\t\t\t\tright: {\n\t\t\t\t\ttable: userTable,\n\t\t\t\t\thydrate: (params, ctx) =>\n\t\t\t\t\t\tusersHydrate(params, ctx as Ctx) as Iterable<unknown>,\n\t\t\t\t\tkey: (user) => userKey(user),\n\t\t\t\t\ton: (user) => userKey(user)\n\t\t\t\t},\n\t\t\t\tselect: (comment, author) => ({ ...comment, author }),\n\t\t\t\tauthorize: (params, ctx) =>\n\t\t\t\t\tcanReadResource(params.resourceId, ctx)\n\t\t\t})\n\t\t];\n\t}\n\n\tif (reactionsConfig !== undefined) {\n\t\tconst allowed = reactionsConfig.allowedEmojis;\n\t\tconst isAllowed = (emoji: string): boolean =>\n\t\t\tallowed === undefined || allowed.length === 0\n\t\t\t\t? true\n\t\t\t\t: allowed.includes(emoji);\n\n\t\tpack.schemas = {\n\t\t\t...(pack.schemas ?? {}),\n\t\t\t[reactionsTable]: {\n\t\t\t\tfields: {\n\t\t\t\t\tactorId: field.string,\n\t\t\t\t\tcommentId: field.string,\n\t\t\t\t\tcreatedAt: field.number,\n\t\t\t\t\temoji: field.string,\n\t\t\t\t\tid: field.string\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t\tpack.readers = {\n\t\t\t...(pack.readers ?? {}),\n\t\t\t[reactionsTable]: reactionReader\n\t\t};\n\t\tpack.writers = {\n\t\t\t...(pack.writers ?? {}),\n\t\t\t[reactionsTable]: reactionWriter\n\t\t};\n\t\tpack.permissions = {\n\t\t\t...(pack.permissions ?? {}),\n\t\t\t[reactionsTable]: {\n\t\t\t\t// Reactions are visible to anyone who can read the comment's\n\t\t\t\t// resource. We look the comment up to gate on canReadResource.\n\t\t\t\tread: (ctx: unknown, row: CommentReactionRow) => {\n\t\t\t\t\tconst comment = store.getById(row.commentId);\n\t\t\t\t\tif (comment === undefined) return false;\n\t\t\t\t\treturn canReadResource(\n\t\t\t\t\t\tcomment.resourceId,\n\t\t\t\t\t\tctx as CollectionContext\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\t// Per-row writes: the row's actorId must match the caller.\n\t\t\t\tinsert: (ctx: unknown, row: CommentReactionRow) => {\n\t\t\t\t\tconst callerId = getActorId(ctx as CollectionContext);\n\t\t\t\t\treturn callerId !== undefined && row.actorId === callerId;\n\t\t\t\t},\n\t\t\t\tupdate: (ctx: unknown, row: CommentReactionRow) => {\n\t\t\t\t\tconst callerId = getActorId(ctx as CollectionContext);\n\t\t\t\t\treturn callerId !== undefined && row.actorId === callerId;\n\t\t\t\t},\n\t\t\t\tdelete: (ctx: unknown, row: CommentReactionRow) => {\n\t\t\t\t\tconst callerId = getActorId(ctx as CollectionContext);\n\t\t\t\t\tif (callerId === undefined) return false;\n\t\t\t\t\tif (\n\t\t\t\t\t\t(row as { actorId?: string }).actorId !== undefined\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn (row as { actorId: string }).actorId === callerId;\n\t\t\t\t\t}\n\t\t\t\t\tconst id = (row as { id?: string }).id;\n\t\t\t\t\tif (id === undefined) return false;\n\t\t\t\t\treturn reactionStore.get(id)?.actorId === callerId;\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\tpack.collections = [\n\t\t\t...(pack.collections ?? []),\n\t\t\tdefineCollection<\n\t\t\t\tCommentReactionRow,\n\t\t\t\t{ commentId: string },\n\t\t\t\tCollectionContext\n\t\t\t>({\n\t\t\t\tname: reactionsCollectionName,\n\t\t\t\ttables: [reactionsTable],\n\t\t\t\tkey: (row) => row.id,\n\t\t\t\thydrate: (params, ctx) => {\n\t\t\t\t\tconst comment = store.getById(params.commentId);\n\t\t\t\t\tif (\n\t\t\t\t\t\tcomment === undefined ||\n\t\t\t\t\t\t!canReadResource(comment.resourceId, ctx)\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn [];\n\t\t\t\t\t}\n\t\t\t\t\treturn (\n\t\t\t\t\t\treactionReader.all(ctx) as CommentReactionRow[]\n\t\t\t\t\t).filter((row) => row.commentId === params.commentId);\n\t\t\t\t},\n\t\t\t\tmatch: (row, params, ctx) => {\n\t\t\t\t\tif (row.commentId !== params.commentId) return false;\n\t\t\t\t\tconst comment = store.getById(params.commentId);\n\t\t\t\t\treturn (\n\t\t\t\t\t\tcomment !== undefined &&\n\t\t\t\t\t\tcanReadResource(comment.resourceId, ctx)\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\tauthorize: (params, ctx) => {\n\t\t\t\t\tconst comment = store.getById(params.commentId);\n\t\t\t\t\treturn (\n\t\t\t\t\t\tcomment !== undefined &&\n\t\t\t\t\t\tcanReadResource(comment.resourceId, ctx)\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t})\n\t\t];\n\n\t\tpack.mutations = [\n\t\t\t...(pack.mutations ?? []),\n\t\t\tdefineMutation<\n\t\t\t\t{ commentId: string; emoji: string },\n\t\t\t\tCollectionContext,\n\t\t\t\tCommentReactionRow\n\t\t\t>({\n\t\t\t\tname: reactMutationName,\n\t\t\t\thandler: async (args, ctx, actions) => {\n\t\t\t\t\tif (!isAllowed(args.emoji)) {\n\t\t\t\t\t\tthrow new CommentReactionNotAllowedError(args.emoji);\n\t\t\t\t\t}\n\t\t\t\t\tconst actorId = resolveActor(getActorId, ctx);\n\t\t\t\t\tconst comment = store.getById(args.commentId);\n\t\t\t\t\tif (comment === undefined) {\n\t\t\t\t\t\tthrow new UnauthorizedError(\n\t\t\t\t\t\t\t`comments:react on missing comment \"${args.commentId}\"`\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (!canReadResource(comment.resourceId, ctx)) {\n\t\t\t\t\t\tthrow new UnauthorizedError(\n\t\t\t\t\t\t\t`comments:react on comment \"${args.commentId}\" (no read access)`\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tconst id = reactionId(args.commentId, actorId, args.emoji);\n\t\t\t\t\tconst existing = reactionStore.get(id);\n\t\t\t\t\tif (existing !== undefined) return existing;\n\t\t\t\t\tconst row: CommentReactionRow = {\n\t\t\t\t\t\tactorId,\n\t\t\t\t\t\tcommentId: args.commentId,\n\t\t\t\t\t\tcreatedAt: now(),\n\t\t\t\t\t\temoji: args.emoji,\n\t\t\t\t\t\tid\n\t\t\t\t\t};\n\t\t\t\t\treturn (await actions.insert(\n\t\t\t\t\t\treactionsTable,\n\t\t\t\t\t\trow\n\t\t\t\t\t)) as CommentReactionRow;\n\t\t\t\t}\n\t\t\t}),\n\t\t\tdefineMutation<\n\t\t\t\t{ commentId: string; emoji: string },\n\t\t\t\tCollectionContext,\n\t\t\t\tvoid\n\t\t\t>({\n\t\t\t\tname: unreactMutationName,\n\t\t\t\thandler: async (args, ctx, actions) => {\n\t\t\t\t\tconst actorId = resolveActor(getActorId, ctx);\n\t\t\t\t\tconst id = reactionId(args.commentId, actorId, args.emoji);\n\t\t\t\t\tif (reactionStore.get(id) === undefined) return;\n\t\t\t\t\tawait actions.delete(reactionsTable, { id });\n\t\t\t\t}\n\t\t\t}),\n\t\t\tdefineMutation<\n\t\t\t\t{ commentId: string; emoji: string },\n\t\t\t\tCollectionContext,\n\t\t\t\t{ reacted: boolean }\n\t\t\t>({\n\t\t\t\tname: toggleReactionMutationName,\n\t\t\t\thandler: async (args, ctx, actions) => {\n\t\t\t\t\tif (!isAllowed(args.emoji)) {\n\t\t\t\t\t\tthrow new CommentReactionNotAllowedError(args.emoji);\n\t\t\t\t\t}\n\t\t\t\t\tconst actorId = resolveActor(getActorId, ctx);\n\t\t\t\t\tconst comment = store.getById(args.commentId);\n\t\t\t\t\tif (comment === undefined) {\n\t\t\t\t\t\tthrow new UnauthorizedError(\n\t\t\t\t\t\t\t`comments:toggleReaction on missing comment \"${args.commentId}\"`\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (!canReadResource(comment.resourceId, ctx)) {\n\t\t\t\t\t\tthrow new UnauthorizedError(\n\t\t\t\t\t\t\t`comments:toggleReaction on comment \"${args.commentId}\" (no read access)`\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tconst id = reactionId(args.commentId, actorId, args.emoji);\n\t\t\t\t\tconst existing = reactionStore.get(id);\n\t\t\t\t\tif (existing === undefined) {\n\t\t\t\t\t\tconst row: CommentReactionRow = {\n\t\t\t\t\t\t\tactorId,\n\t\t\t\t\t\t\tcommentId: args.commentId,\n\t\t\t\t\t\t\tcreatedAt: now(),\n\t\t\t\t\t\t\temoji: args.emoji,\n\t\t\t\t\t\t\tid\n\t\t\t\t\t\t};\n\t\t\t\t\t\tawait actions.insert(reactionsTable, row);\n\t\t\t\t\t\treturn { reacted: true };\n\t\t\t\t\t}\n\t\t\t\t\tawait actions.delete(reactionsTable, { id });\n\t\t\t\t\treturn { reacted: false };\n\t\t\t\t}\n\t\t\t})\n\t\t];\n\t}\n\n\treturn defineSyncPack(pack);\n};\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": "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",
|
|
7
|
+
"mappings": "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",
|
|
8
8
|
"debugId": "041BEB316C29DEDC64756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@absolutejs/sync-pack-comments",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Threaded comments pack for @absolutejs/sync — per-resource ACL-scoped, author/moderator gates, optional CRDT bodies",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"@absolutejs/sync": ">=2.11.0 <3"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@absolutejs/sync": "2.
|
|
42
|
+
"@absolutejs/sync": "2.29.0",
|
|
43
43
|
"@types/bun": "1.3.14",
|
|
44
44
|
"prettier": "3.5.3",
|
|
45
45
|
"typescript": "5.8.3"
|
|
@@ -81,7 +81,10 @@
|
|
|
81
81
|
"match": "*comment*",
|
|
82
82
|
"sensitivity": "private",
|
|
83
83
|
"protection": "required",
|
|
84
|
-
"onProtectionUnavailable": "memory-only"
|
|
84
|
+
"onProtectionUnavailable": "memory-only",
|
|
85
|
+
"conflict": {
|
|
86
|
+
"strategy": "manual"
|
|
87
|
+
}
|
|
85
88
|
}
|
|
86
89
|
]
|
|
87
90
|
}
|