@absolutejs/sync-pack-mentions 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4 -19
- package/dist/index.js.map +3 -3
- package/dist/manifest.js +139 -229
- package/dist/manifest.js.map +2 -2
- package/package.json +8 -4
package/dist/index.d.ts
CHANGED
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
* }));
|
|
33
33
|
* ```
|
|
34
34
|
*/
|
|
35
|
-
import { type CollectionContext, type MutationActions, type SyncPack, type TableReader, type TableWriter } from
|
|
35
|
+
import { type CollectionContext, type MutationActions, type SyncPack, type TableReader, type TableWriter } from '@absolutejs/sync/engine';
|
|
36
36
|
/** One row per `(sourceKind, sourceId, mentionedActorId)`. */
|
|
37
37
|
export type MentionRow = {
|
|
38
38
|
/** `${sourceKind}:${sourceId}:${mentionedActorId}` — deterministic so re-recording is idempotent. */
|
package/dist/index.js
CHANGED
|
@@ -1,19 +1,4 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __returnValue = (v) => v;
|
|
4
|
-
function __exportSetter(name, newValue) {
|
|
5
|
-
this[name] = __returnValue.bind(null, newValue);
|
|
6
|
-
}
|
|
7
|
-
var __export = (target, all) => {
|
|
8
|
-
for (var name in all)
|
|
9
|
-
__defProp(target, name, {
|
|
10
|
-
get: all[name],
|
|
11
|
-
enumerable: true,
|
|
12
|
-
configurable: true,
|
|
13
|
-
set: __exportSetter.bind(all, name)
|
|
14
|
-
});
|
|
15
|
-
};
|
|
16
|
-
|
|
17
2
|
// src/index.ts
|
|
18
3
|
import {
|
|
19
4
|
defineCollection,
|
|
@@ -240,11 +225,11 @@ var createMentionsPack = (config = {}) => {
|
|
|
240
225
|
});
|
|
241
226
|
};
|
|
242
227
|
export {
|
|
243
|
-
|
|
244
|
-
createMentionsPack,
|
|
228
|
+
DEFAULT_MENTION_PATTERN,
|
|
245
229
|
createInMemoryMentionsStore,
|
|
246
|
-
|
|
230
|
+
createMentionsPack,
|
|
231
|
+
parseMentions
|
|
247
232
|
};
|
|
248
233
|
|
|
249
|
-
//# debugId=
|
|
234
|
+
//# debugId=B52EA99BED16BE0964756E2164756E21
|
|
250
235
|
//# sourceMappingURL=index.js.map
|
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-mentions` — `@username` parser pack for\n * `@absolutejs/sync`. Owns a `mentions` table, exposes a\n * `mentions:record` mutation that parses a body, writes per-actor\n * mention rows, and fires an `onMention` hook the host can use to\n * compose with other packs (notifications, email, Slack, …).\n *\n * Composition model: packs never call each other's mutations directly.\n * Instead, the host wires them. This pack provides the parser + the\n * per-actor collection; the `onMention` hook is the seam — typically\n * `({ mention }, ctx, actions) => engine.runMutation('notifications:notify', …)`.\n * The pack itself only owns the mentions table.\n *\n * @example\n * ```ts\n * import { createSyncEngine } from '@absolutejs/sync/engine';\n * import { createMentionsPack } from '@absolutejs/sync-pack-mentions';\n *\n * const engine = createSyncEngine();\n * engine.registerPack(createMentionsPack({\n * getActorId: (ctx) => ctx.session.userId,\n * resolveActorId: async (username) => userIdByUsername(username),\n * onMention: async ({ mention }, ctx) => {\n * await engine.runMutation('notifications:notify', {\n * targetActorId: mention.mentionedActorId,\n * kind: 'mention',\n * title: `You were mentioned`,\n * body: mention.snippet,\n * href: `/comments/${mention.sourceId}`,\n * }, ctx);\n * },\n * }));\n * ```\n */\n\nimport {\n defineCollection,\n defineMutation,\n defineSchema,\n defineSyncPack,\n field,\n UnauthorizedError,\n type CollectionContext,\n type MutationActions,\n type SyncPack,\n type TableReader,\n type TableWriter,\n} from \"@absolutejs/sync/engine\";\n\n/** One row per `(sourceKind, sourceId, mentionedActorId)`. */\nexport type MentionRow = {\n /** `${sourceKind}:${sourceId}:${mentionedActorId}` — deterministic so re-recording is idempotent. */\n id: string;\n /** Host-level category for the row that originated the mention (\"comment\", \"doc\", \"task\", …). */\n sourceKind: string;\n /** Host-level id of the row that originated the mention. */\n sourceId: string;\n /** Who wrote the body containing the mention. `null` for system-generated content. */\n authorId: string | null;\n /** The actor id the `@username` resolved to. */\n mentionedActorId: string;\n /** The raw username text (without the `@`). */\n username: string;\n /** A short excerpt of the body around the mention — useful for inbox previews. */\n snippet: string;\n createdAt: number;\n /** Stamped by `mentions:resolve` (host's onMention hook is free to call it). */\n resolvedAt: number | null;\n};\n\nexport type MentionsStore = {\n reader: TableReader<CollectionContext>;\n writer: TableWriter<MentionRow, CollectionContext, unknown>;\n getById: (id: string) => MentionRow | undefined;\n};\n\nexport const createInMemoryMentionsStore = (): MentionsStore => {\n const rows = new Map<string, MentionRow>();\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 MentionRow;\n rows.set(data.id, merged);\n return merged;\n },\n },\n };\n};\n\n/** Default regex — matches `@word` where `word` is one or more `[A-Za-z0-9_-]`. */\nexport const DEFAULT_MENTION_PATTERN = /@([A-Za-z0-9_-]+)/g;\nconst DEFAULT_SNIPPET_RADIUS = 30;\n\n/** Args passed to the host's `onMention` hook. */\nexport type OnMentionArgs = {\n mention: MentionRow;\n /** Full body the mention came from. */\n body: string;\n};\n\nexport type MentionsPackConfig<Ctx = CollectionContext> = {\n prefix?: string;\n getActorId?: (ctx: Ctx) => string | undefined;\n /**\n * Regex used to extract `@usernames` from a body. Must have the `g`\n * flag. Default {@link DEFAULT_MENTION_PATTERN}.\n */\n pattern?: RegExp;\n /**\n * Resolve a matched username to the actor id to record + notify.\n * Returning `undefined` skips that match (unknown user). Default is\n * identity (`username -> username`), useful for tests and for apps\n * where usernames already double as actor ids.\n */\n resolveActorId?: (\n username: string,\n ctx: Ctx,\n ) => Promise<string | undefined> | string | undefined;\n /**\n * How many characters around each match to keep in `snippet`.\n * Default 30 each side; the snippet is clamped to the body length.\n */\n snippetRadius?: number;\n /**\n * Composition seam — fired once per mention after its row is\n * written. The host's typical implementation calls\n * `engine.runMutation('notifications:notify', …)` here. Errors are\n * caught + logged; one failing hook does not roll back the recorded\n * mention row.\n */\n onMention?: (\n args: OnMentionArgs,\n ctx: Ctx,\n actions: MutationActions,\n ) => void | Promise<void>;\n /** Custom store. Default per-instance in-memory. */\n store?: MentionsStore;\n /** Wall-clock. Default `Date.now`. */\n now?: () => number;\n};\n\nconst buildSnippet = (\n body: string,\n matchIndex: number,\n radius: number,\n): string => {\n const start = Math.max(0, matchIndex - radius);\n const end = Math.min(body.length, matchIndex + radius);\n const prefix = start > 0 ? \"…\" : \"\";\n const suffix = end < body.length ? \"…\" : \"\";\n return `${prefix}${body.slice(start, end)}${suffix}`;\n};\n\n/** Extract distinct `@username` matches from a body. Public so tests can lean on it. */\nexport const parseMentions = (\n body: string,\n pattern: RegExp = DEFAULT_MENTION_PATTERN,\n): { username: string; index: number }[] => {\n const seen = new Set<string>();\n const matches: { username: string; index: number }[] = [];\n pattern.lastIndex = 0;\n let match: RegExpExecArray | null = pattern.exec(body);\n while (match !== null) {\n const username = match[1];\n if (username !== undefined && !seen.has(username)) {\n seen.add(username);\n matches.push({ index: match.index, username });\n }\n if (!pattern.global) break;\n match = pattern.exec(body);\n }\n pattern.lastIndex = 0;\n return matches;\n};\n\nconst rowId = (\n sourceKind: string,\n sourceId: string,\n mentionedActorId: string,\n): string => `${sourceKind}:${sourceId}:${mentionedActorId}`;\n\n/**\n * Build a {@link SyncPack} that parses `@mentions` from a posted body\n * and writes one row per mentioned actor. Composes with other packs\n * (notifications, digest, …) via the `onMention` hook — the pack itself\n * never calls another pack's mutations.\n */\nexport const createMentionsPack = <Ctx = CollectionContext>(\n config: MentionsPackConfig<Ctx> = {},\n): SyncPack => {\n const prefix = config.prefix ?? \"\";\n const table = `${prefix}mentions`;\n const collectionName = table;\n const recordMutationName = `${prefix}mentions:record`;\n const resolveMutationName = `${prefix}mentions:resolve`;\n const dismissMutationName = `${prefix}mentions:dismiss`;\n const store = config.store ?? createInMemoryMentionsStore();\n const now = config.now ?? Date.now;\n const pattern = config.pattern ?? DEFAULT_MENTION_PATTERN;\n const snippetRadius = config.snippetRadius ?? DEFAULT_SNIPPET_RADIUS;\n const getActorId = (config.getActorId ??\n ((ctx: CollectionContext) => (ctx as { userId?: string }).userId)) as (\n ctx: CollectionContext,\n ) => string | undefined;\n const resolveActorId = (config.resolveActorId ??\n ((username: string) => username)) as (\n username: string,\n ctx: CollectionContext,\n ) => Promise<string | undefined> | string | undefined;\n const onMention = config.onMention as\n | ((\n args: OnMentionArgs,\n ctx: CollectionContext,\n actions: MutationActions,\n ) => void | Promise<void>)\n | undefined;\n\n type Params = { sourceKind?: string; unresolvedOnly?: boolean };\n\n return defineSyncPack({\n name: \"@absolutejs/sync-pack-mentions\",\n ownsTables: [table],\n readsTables: [],\n version: \"0.1.0\",\n\n schemas: defineSchema({\n [table]: {\n fields: {\n id: field.string,\n sourceKind: field.string,\n sourceId: field.string,\n authorId: (value) => value === null || typeof value === \"string\",\n mentionedActorId: field.string,\n username: field.string,\n snippet: field.string,\n createdAt: field.number,\n resolvedAt: (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: MentionRow) => {\n const callerId = getActorId(ctx as CollectionContext);\n return callerId !== undefined && row.mentionedActorId === callerId;\n },\n // `mentions:record` is host-trusted (the record mutation\n // parses + inserts on behalf of the author). Allow any\n // insert; the recorded row's mentionedActorId comes from\n // the resolveActorId callback the host configured.\n insert: () => true,\n update: (ctx: unknown, row: MentionRow) => {\n const callerId = getActorId(ctx as CollectionContext);\n return callerId !== undefined && row.mentionedActorId === callerId;\n },\n delete: (ctx: unknown, row: MentionRow) => {\n const callerId = getActorId(ctx as CollectionContext);\n if (callerId === undefined) return false;\n const ownerId =\n (row as { mentionedActorId?: string }).mentionedActorId ??\n (() => {\n const id = (row as { id?: string }).id;\n return id === undefined\n ? undefined\n : store.getById(id)?.mentionedActorId;\n })();\n return ownerId === callerId;\n },\n },\n },\n\n collections: [\n defineCollection<MentionRow, Params, CollectionContext>({\n name: collectionName,\n tables: [table],\n key: (row) => row.id,\n hydrate: (params, ctx) => {\n const callerId = getActorId(ctx);\n if (callerId === undefined) return [];\n return (store.reader.all(ctx) as MentionRow[]).filter(\n (row) =>\n row.mentionedActorId === callerId &&\n (params.sourceKind === undefined ||\n row.sourceKind === params.sourceKind) &&\n (params.unresolvedOnly !== true || row.resolvedAt === null),\n );\n },\n match: (row, params, ctx) => {\n const callerId = getActorId(ctx);\n if (callerId === undefined || row.mentionedActorId !== callerId) {\n return false;\n }\n return (\n (params.sourceKind === undefined ||\n row.sourceKind === params.sourceKind) &&\n (params.unresolvedOnly !== true || row.resolvedAt === null)\n );\n },\n authorize: (_params, ctx) =>\n getActorId(ctx as CollectionContext) !== undefined,\n }),\n ],\n\n mutations: [\n // mentions:record { sourceKind, sourceId, body, authorId? } —\n // host calls this RIGHT AFTER its own post mutation succeeds.\n // The pack parses `@usernames`, resolves them to actor ids,\n // writes one mention row per actor (idempotent on re-record),\n // and fires `onMention` per row so the host can compose with\n // other packs (notifications, email, …).\n defineMutation<\n {\n sourceKind: string;\n sourceId: string;\n body: string;\n authorId?: string | null;\n },\n CollectionContext,\n MentionRow[]\n >({\n name: recordMutationName,\n handler: async (args, ctx, actions) => {\n const authorIdInput =\n args.authorId === undefined\n ? (getActorId(ctx as CollectionContext) ?? null)\n : args.authorId;\n const matches = parseMentions(args.body, pattern);\n const written: MentionRow[] = [];\n for (const { username, index } of matches) {\n const resolved = await resolveActorId(\n username,\n ctx as CollectionContext,\n );\n if (resolved === undefined) continue;\n if (authorIdInput !== null && resolved === authorIdInput) {\n continue;\n }\n const id = rowId(args.sourceKind, args.sourceId, resolved);\n const existing = store.getById(id);\n if (existing !== undefined) {\n written.push(existing);\n continue;\n }\n const row: MentionRow = {\n authorId: authorIdInput,\n createdAt: now(),\n id,\n mentionedActorId: resolved,\n resolvedAt: null,\n snippet: buildSnippet(args.body, index, snippetRadius),\n sourceId: args.sourceId,\n sourceKind: args.sourceKind,\n username,\n };\n const inserted = (await actions.insert(table, row)) as MentionRow;\n written.push(inserted);\n if (onMention !== undefined) {\n try {\n await onMention(\n { body: args.body, mention: inserted },\n ctx as CollectionContext,\n actions as MutationActions,\n );\n } catch (error) {\n console.error(\n \"[sync-pack-mentions] onMention hook threw:\",\n error,\n );\n }\n }\n }\n return written;\n },\n }),\n defineMutation<{ id: string }, CollectionContext, MentionRow | undefined>(\n {\n name: resolveMutationName,\n handler: async (args, ctx, actions) => {\n const callerId = getActorId(ctx as CollectionContext);\n if (callerId === undefined) {\n throw new UnauthorizedError(\"mentions:resolve (no actor id)\");\n }\n const existing = store.getById(args.id);\n if (existing === undefined) return undefined;\n if (existing.mentionedActorId !== callerId) {\n throw new UnauthorizedError(\n \"mentions:resolve (not the mentioned actor)\",\n );\n }\n if (existing.resolvedAt !== null) return existing;\n return (await actions.update(table, {\n ...existing,\n resolvedAt: now(),\n })) as MentionRow;\n },\n },\n ),\n defineMutation<{ id: string }, CollectionContext, void>({\n name: dismissMutationName,\n handler: async (args, ctx, actions) => {\n const callerId = getActorId(ctx as CollectionContext);\n if (callerId === undefined) {\n throw new UnauthorizedError(\"mentions:dismiss (no actor id)\");\n }\n const existing = store.getById(args.id);\n if (existing === undefined) return;\n if (existing.mentionedActorId !== callerId) {\n throw new UnauthorizedError(\n \"mentions:dismiss (not the mentioned actor)\",\n );\n }\n await actions.delete(table, { id: args.id });\n },\n }),\n ],\n });\n};\n"
|
|
5
|
+
"/**\n * `@absolutejs/sync-pack-mentions` — `@username` parser pack for\n * `@absolutejs/sync`. Owns a `mentions` table, exposes a\n * `mentions:record` mutation that parses a body, writes per-actor\n * mention rows, and fires an `onMention` hook the host can use to\n * compose with other packs (notifications, email, Slack, …).\n *\n * Composition model: packs never call each other's mutations directly.\n * Instead, the host wires them. This pack provides the parser + the\n * per-actor collection; the `onMention` hook is the seam — typically\n * `({ mention }, ctx, actions) => engine.runMutation('notifications:notify', …)`.\n * The pack itself only owns the mentions table.\n *\n * @example\n * ```ts\n * import { createSyncEngine } from '@absolutejs/sync/engine';\n * import { createMentionsPack } from '@absolutejs/sync-pack-mentions';\n *\n * const engine = createSyncEngine();\n * engine.registerPack(createMentionsPack({\n * getActorId: (ctx) => ctx.session.userId,\n * resolveActorId: async (username) => userIdByUsername(username),\n * onMention: async ({ mention }, ctx) => {\n * await engine.runMutation('notifications:notify', {\n * targetActorId: mention.mentionedActorId,\n * kind: 'mention',\n * title: `You were mentioned`,\n * body: mention.snippet,\n * href: `/comments/${mention.sourceId}`,\n * }, ctx);\n * },\n * }));\n * ```\n */\n\nimport {\n\tdefineCollection,\n\tdefineMutation,\n\tdefineSchema,\n\tdefineSyncPack,\n\tfield,\n\tUnauthorizedError,\n\ttype CollectionContext,\n\ttype MutationActions,\n\ttype SyncPack,\n\ttype TableReader,\n\ttype TableWriter\n} from '@absolutejs/sync/engine';\n\n/** One row per `(sourceKind, sourceId, mentionedActorId)`. */\nexport type MentionRow = {\n\t/** `${sourceKind}:${sourceId}:${mentionedActorId}` — deterministic so re-recording is idempotent. */\n\tid: string;\n\t/** Host-level category for the row that originated the mention (\"comment\", \"doc\", \"task\", …). */\n\tsourceKind: string;\n\t/** Host-level id of the row that originated the mention. */\n\tsourceId: string;\n\t/** Who wrote the body containing the mention. `null` for system-generated content. */\n\tauthorId: string | null;\n\t/** The actor id the `@username` resolved to. */\n\tmentionedActorId: string;\n\t/** The raw username text (without the `@`). */\n\tusername: string;\n\t/** A short excerpt of the body around the mention — useful for inbox previews. */\n\tsnippet: string;\n\tcreatedAt: number;\n\t/** Stamped by `mentions:resolve` (host's onMention hook is free to call it). */\n\tresolvedAt: number | null;\n};\n\nexport type MentionsStore = {\n\treader: TableReader<CollectionContext>;\n\twriter: TableWriter<MentionRow, CollectionContext, unknown>;\n\tgetById: (id: string) => MentionRow | undefined;\n};\n\nexport const createInMemoryMentionsStore = (): MentionsStore => {\n\tconst rows = new Map<string, MentionRow>();\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 MentionRow;\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/** Default regex — matches `@word` where `word` is one or more `[A-Za-z0-9_-]`. */\nexport const DEFAULT_MENTION_PATTERN = /@([A-Za-z0-9_-]+)/g;\nconst DEFAULT_SNIPPET_RADIUS = 30;\n\n/** Args passed to the host's `onMention` hook. */\nexport type OnMentionArgs = {\n\tmention: MentionRow;\n\t/** Full body the mention came from. */\n\tbody: string;\n};\n\nexport type MentionsPackConfig<Ctx = CollectionContext> = {\n\tprefix?: string;\n\tgetActorId?: (ctx: Ctx) => string | undefined;\n\t/**\n\t * Regex used to extract `@usernames` from a body. Must have the `g`\n\t * flag. Default {@link DEFAULT_MENTION_PATTERN}.\n\t */\n\tpattern?: RegExp;\n\t/**\n\t * Resolve a matched username to the actor id to record + notify.\n\t * Returning `undefined` skips that match (unknown user). Default is\n\t * identity (`username -> username`), useful for tests and for apps\n\t * where usernames already double as actor ids.\n\t */\n\tresolveActorId?: (\n\t\tusername: string,\n\t\tctx: Ctx\n\t) => Promise<string | undefined> | string | undefined;\n\t/**\n\t * How many characters around each match to keep in `snippet`.\n\t * Default 30 each side; the snippet is clamped to the body length.\n\t */\n\tsnippetRadius?: number;\n\t/**\n\t * Composition seam — fired once per mention after its row is\n\t * written. The host's typical implementation calls\n\t * `engine.runMutation('notifications:notify', …)` here. Errors are\n\t * caught + logged; one failing hook does not roll back the recorded\n\t * mention row.\n\t */\n\tonMention?: (\n\t\targs: OnMentionArgs,\n\t\tctx: Ctx,\n\t\tactions: MutationActions\n\t) => void | Promise<void>;\n\t/** Custom store. Default per-instance in-memory. */\n\tstore?: MentionsStore;\n\t/** Wall-clock. Default `Date.now`. */\n\tnow?: () => number;\n};\n\nconst buildSnippet = (body: string, matchIndex: number, radius: number): string => {\n\tconst start = Math.max(0, matchIndex - radius);\n\tconst end = Math.min(body.length, matchIndex + radius);\n\tconst prefix = start > 0 ? '…' : '';\n\tconst suffix = end < body.length ? '…' : '';\n\treturn `${prefix}${body.slice(start, end)}${suffix}`;\n};\n\n/** Extract distinct `@username` matches from a body. Public so tests can lean on it. */\nexport const parseMentions = (\n\tbody: string,\n\tpattern: RegExp = DEFAULT_MENTION_PATTERN\n): { username: string; index: number }[] => {\n\tconst seen = new Set<string>();\n\tconst matches: { username: string; index: number }[] = [];\n\tpattern.lastIndex = 0;\n\tlet match: RegExpExecArray | null = pattern.exec(body);\n\twhile (match !== null) {\n\t\tconst username = match[1];\n\t\tif (username !== undefined && !seen.has(username)) {\n\t\t\tseen.add(username);\n\t\t\tmatches.push({ index: match.index, username });\n\t\t}\n\t\tif (!pattern.global) break;\n\t\tmatch = pattern.exec(body);\n\t}\n\tpattern.lastIndex = 0;\n\treturn matches;\n};\n\nconst rowId = (\n\tsourceKind: string,\n\tsourceId: string,\n\tmentionedActorId: string\n): string => `${sourceKind}:${sourceId}:${mentionedActorId}`;\n\n/**\n * Build a {@link SyncPack} that parses `@mentions` from a posted body\n * and writes one row per mentioned actor. Composes with other packs\n * (notifications, digest, …) via the `onMention` hook — the pack itself\n * never calls another pack's mutations.\n */\nexport const createMentionsPack = <Ctx = CollectionContext>(\n\tconfig: MentionsPackConfig<Ctx> = {}\n): SyncPack => {\n\tconst prefix = config.prefix ?? '';\n\tconst table = `${prefix}mentions`;\n\tconst collectionName = table;\n\tconst recordMutationName = `${prefix}mentions:record`;\n\tconst resolveMutationName = `${prefix}mentions:resolve`;\n\tconst dismissMutationName = `${prefix}mentions:dismiss`;\n\tconst store = config.store ?? createInMemoryMentionsStore();\n\tconst now = config.now ?? Date.now;\n\tconst pattern = config.pattern ?? DEFAULT_MENTION_PATTERN;\n\tconst snippetRadius = config.snippetRadius ?? DEFAULT_SNIPPET_RADIUS;\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 resolveActorId = (config.resolveActorId ??\n\t\t((username: string) => username)) as (\n\t\tusername: string,\n\t\tctx: CollectionContext\n\t) => Promise<string | undefined> | string | undefined;\n\tconst onMention = config.onMention as\n\t\t| ((\n\t\t\t\targs: OnMentionArgs,\n\t\t\t\tctx: CollectionContext,\n\t\t\t\tactions: MutationActions\n\t\t ) => void | Promise<void>)\n\t\t| undefined;\n\n\ttype Params = { sourceKind?: string; unresolvedOnly?: boolean };\n\n\treturn defineSyncPack({\n\t\tname: '@absolutejs/sync-pack-mentions',\n\t\townsTables: [table],\n\t\treadsTables: [],\n\t\tversion: '0.1.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\tsourceKind: field.string,\n\t\t\t\t\tsourceId: field.string,\n\t\t\t\t\tauthorId: (value) =>\n\t\t\t\t\t\tvalue === null || typeof value === 'string',\n\t\t\t\t\tmentionedActorId: field.string,\n\t\t\t\t\tusername: field.string,\n\t\t\t\t\tsnippet: field.string,\n\t\t\t\t\tcreatedAt: field.number,\n\t\t\t\t\tresolvedAt: (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: MentionRow) => {\n\t\t\t\t\tconst callerId = getActorId(ctx as CollectionContext);\n\t\t\t\t\treturn callerId !== undefined && row.mentionedActorId === callerId;\n\t\t\t\t},\n\t\t\t\t// `mentions:record` is host-trusted (the record mutation\n\t\t\t\t// parses + inserts on behalf of the author). Allow any\n\t\t\t\t// insert; the recorded row's mentionedActorId comes from\n\t\t\t\t// the resolveActorId callback the host configured.\n\t\t\t\tinsert: () => true,\n\t\t\t\tupdate: (ctx: unknown, row: MentionRow) => {\n\t\t\t\t\tconst callerId = getActorId(ctx as CollectionContext);\n\t\t\t\t\treturn callerId !== undefined && row.mentionedActorId === callerId;\n\t\t\t\t},\n\t\t\t\tdelete: (ctx: unknown, row: MentionRow) => {\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 ownerId =\n\t\t\t\t\t\t(row as { mentionedActorId?: string }).mentionedActorId ??\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)?.mentionedActorId;\n\t\t\t\t\t\t})();\n\t\t\t\t\treturn ownerId === callerId;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\tcollections: [\n\t\t\tdefineCollection<MentionRow, 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\tconst callerId = getActorId(ctx);\n\t\t\t\t\tif (callerId === undefined) return [];\n\t\t\t\t\treturn (store.reader.all(ctx) as MentionRow[]).filter(\n\t\t\t\t\t\t(row) =>\n\t\t\t\t\t\t\trow.mentionedActorId === callerId &&\n\t\t\t\t\t\t\t(params.sourceKind === undefined ||\n\t\t\t\t\t\t\t\trow.sourceKind === params.sourceKind) &&\n\t\t\t\t\t\t\t(params.unresolvedOnly !== true ||\n\t\t\t\t\t\t\t\trow.resolvedAt === null)\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\tmatch: (row, params, ctx) => {\n\t\t\t\t\tconst callerId = getActorId(ctx);\n\t\t\t\t\tif (\n\t\t\t\t\t\tcallerId === undefined ||\n\t\t\t\t\t\trow.mentionedActorId !== callerId\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\treturn (\n\t\t\t\t\t\t(params.sourceKind === undefined ||\n\t\t\t\t\t\t\trow.sourceKind === params.sourceKind) &&\n\t\t\t\t\t\t(params.unresolvedOnly !== true || row.resolvedAt === null)\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\tauthorize: (_params, ctx) =>\n\t\t\t\t\tgetActorId(ctx as CollectionContext) !== undefined\n\t\t\t})\n\t\t],\n\n\t\tmutations: [\n\t\t\t// mentions:record { sourceKind, sourceId, body, authorId? } —\n\t\t\t// host calls this RIGHT AFTER its own post mutation succeeds.\n\t\t\t// The pack parses `@usernames`, resolves them to actor ids,\n\t\t\t// writes one mention row per actor (idempotent on re-record),\n\t\t\t// and fires `onMention` per row so the host can compose with\n\t\t\t// other packs (notifications, email, …).\n\t\t\tdefineMutation<\n\t\t\t\t{\n\t\t\t\t\tsourceKind: string;\n\t\t\t\t\tsourceId: string;\n\t\t\t\t\tbody: string;\n\t\t\t\t\tauthorId?: string | null;\n\t\t\t\t},\n\t\t\t\tCollectionContext,\n\t\t\t\tMentionRow[]\n\t\t\t>({\n\t\t\t\tname: recordMutationName,\n\t\t\t\thandler: async (args, ctx, actions) => {\n\t\t\t\t\tconst authorIdInput =\n\t\t\t\t\t\targs.authorId === undefined\n\t\t\t\t\t\t\t? getActorId(ctx as CollectionContext) ?? null\n\t\t\t\t\t\t\t: args.authorId;\n\t\t\t\t\tconst matches = parseMentions(args.body, pattern);\n\t\t\t\t\tconst written: MentionRow[] = [];\n\t\t\t\t\tfor (const { username, index } of matches) {\n\t\t\t\t\t\tconst resolved = await resolveActorId(\n\t\t\t\t\t\t\tusername,\n\t\t\t\t\t\t\tctx as CollectionContext\n\t\t\t\t\t\t);\n\t\t\t\t\t\tif (resolved === undefined) continue;\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tauthorIdInput !== null &&\n\t\t\t\t\t\t\tresolved === authorIdInput\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst id = rowId(\n\t\t\t\t\t\t\targs.sourceKind,\n\t\t\t\t\t\t\targs.sourceId,\n\t\t\t\t\t\t\tresolved\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst existing = store.getById(id);\n\t\t\t\t\t\tif (existing !== undefined) {\n\t\t\t\t\t\t\twritten.push(existing);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst row: MentionRow = {\n\t\t\t\t\t\t\tauthorId: authorIdInput,\n\t\t\t\t\t\t\tcreatedAt: now(),\n\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\tmentionedActorId: resolved,\n\t\t\t\t\t\t\tresolvedAt: null,\n\t\t\t\t\t\t\tsnippet: buildSnippet(\n\t\t\t\t\t\t\t\targs.body,\n\t\t\t\t\t\t\t\tindex,\n\t\t\t\t\t\t\t\tsnippetRadius\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tsourceId: args.sourceId,\n\t\t\t\t\t\t\tsourceKind: args.sourceKind,\n\t\t\t\t\t\t\tusername\n\t\t\t\t\t\t};\n\t\t\t\t\t\tconst inserted = (await actions.insert(\n\t\t\t\t\t\t\ttable,\n\t\t\t\t\t\t\trow\n\t\t\t\t\t\t)) as MentionRow;\n\t\t\t\t\t\twritten.push(inserted);\n\t\t\t\t\t\tif (onMention !== undefined) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tawait onMention(\n\t\t\t\t\t\t\t\t\t{ body: args.body, mention: inserted },\n\t\t\t\t\t\t\t\t\tctx as CollectionContext,\n\t\t\t\t\t\t\t\t\tactions as MutationActions\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\t\t\t'[sync-pack-mentions] onMention hook threw:',\n\t\t\t\t\t\t\t\t\terror\n\t\t\t\t\t\t\t\t);\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\treturn written;\n\t\t\t\t}\n\t\t\t}),\n\t\t\tdefineMutation<\n\t\t\t\t{ id: string },\n\t\t\t\tCollectionContext,\n\t\t\t\tMentionRow | undefined\n\t\t\t>({\n\t\t\t\tname: resolveMutationName,\n\t\t\t\thandler: async (args, ctx, actions) => {\n\t\t\t\t\tconst callerId = getActorId(ctx as CollectionContext);\n\t\t\t\t\tif (callerId === undefined) {\n\t\t\t\t\t\tthrow new UnauthorizedError(\n\t\t\t\t\t\t\t'mentions:resolve (no actor id)'\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tconst existing = store.getById(args.id);\n\t\t\t\t\tif (existing === undefined) return undefined;\n\t\t\t\t\tif (existing.mentionedActorId !== callerId) {\n\t\t\t\t\t\tthrow new UnauthorizedError(\n\t\t\t\t\t\t\t'mentions:resolve (not the mentioned actor)'\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (existing.resolvedAt !== null) return existing;\n\t\t\t\t\treturn (await actions.update(table, {\n\t\t\t\t\t\t...existing,\n\t\t\t\t\t\tresolvedAt: now()\n\t\t\t\t\t})) as MentionRow;\n\t\t\t\t}\n\t\t\t}),\n\t\t\tdefineMutation<{ id: string }, CollectionContext, void>({\n\t\t\t\tname: dismissMutationName,\n\t\t\t\thandler: async (args, ctx, actions) => {\n\t\t\t\t\tconst callerId = getActorId(ctx as CollectionContext);\n\t\t\t\t\tif (callerId === undefined) {\n\t\t\t\t\t\tthrow new UnauthorizedError(\n\t\t\t\t\t\t\t'mentions:dismiss (no actor id)'\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tconst existing = store.getById(args.id);\n\t\t\t\t\tif (existing === undefined) return;\n\t\t\t\t\tif (existing.mentionedActorId !== callerId) {\n\t\t\t\t\t\tthrow new UnauthorizedError(\n\t\t\t\t\t\t\t'mentions:dismiss (not the mentioned actor)'\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tawait actions.delete(table, { id: args.id });\n\t\t\t\t}\n\t\t\t})\n\t\t]\n\t});\n};\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": "
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "B52EA99BED16BE0964756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|