@absolutejs/sync-pack-favorites 0.2.5 → 0.4.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 CHANGED
@@ -11,7 +11,7 @@
11
11
  * the comments pack). Useful when the UI wants "title + body + favorited?"
12
12
  * in one subscription.
13
13
  */
14
- import { type CollectionContext, type SyncPack, type TableReader, type TableWriter } from "@absolutejs/sync/engine";
14
+ import { type CollectionContext, type SyncPack, type TableReader, type TableWriter } from '@absolutejs/sync/engine';
15
15
  /** A single favorited resource for a single actor. */
16
16
  export type FavoriteRow = {
17
17
  /** `${actorId}:${resourceKind}:${resourceId}` — deterministic so toggling is idempotent. */
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-favorites` — per-actor saved resources.\n *\n * Owns a `favorites` table; rows are `(actorId, resourceKind, resourceId)`\n * triples with a stable primary key derived from them, so toggling\n * favorite/unfavorite is an upsert/delete with no extra bookkeeping.\n *\n * Each actor sees only their own rows. Optional `joinResources` registers\n * a `favorites-with-resource` join collection that pairs each favorite\n * with the host's resource row (same shape as comments-with-author from\n * the comments pack). Useful when the UI wants \"title + body + favorited?\"\n * in one subscription.\n */\n\nimport {\n defineCollection,\n defineJoinCollection,\n defineMutation,\n defineSchema,\n defineSyncPack,\n field,\n UnauthorizedError,\n type CollectionContext,\n type SyncPack,\n type TableReader,\n type TableWriter,\n} from \"@absolutejs/sync/engine\";\n\n/** A single favorited resource for a single actor. */\nexport type FavoriteRow = {\n /** `${actorId}:${resourceKind}:${resourceId}` — deterministic so toggling is idempotent. */\n id: string;\n actorId: string;\n /** App-level resource type (\"doc\", \"task\", \"issue\", ...). Lets one inbox span multiple kinds. */\n resourceKind: string;\n resourceId: string;\n createdAt: number;\n /**\n * When the actor pinned this favorite, or `null` when unpinned.\n * Clients can sort pinned-first by descending `pinnedAt`. Set by the\n * `favorites:pin` / `favorites:togglePin` mutations.\n */\n pinnedAt: number | null;\n};\n\n/** A `FavoriteRow` paired with the host's resource row, from the optional join. */\nexport type FavoriteWithResource<ResourceRow = unknown> = FavoriteRow & {\n resource: ResourceRow;\n};\n\nexport type FavoritesStore = {\n reader: TableReader<CollectionContext>;\n writer: TableWriter<FavoriteRow, CollectionContext, unknown>;\n getById: (id: string) => FavoriteRow | undefined;\n};\n\nexport const createInMemoryFavoritesStore = (): FavoritesStore => {\n const rows = new Map<string, FavoriteRow>();\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 FavoriteRow;\n rows.set(data.id, merged);\n return merged;\n },\n },\n };\n};\n\nexport type JoinResourcesConfig<Ctx, ResourceRow = unknown> = {\n /**\n * The host's resource table name. Surfaced in `readsTables` so the\n * dependency graph is reviewable. Required when the join is enabled\n * (no default — many apps have multiple resource tables).\n */\n table: string;\n /** Get the host resource's id (the join field). Default `(row) => row.id`. */\n key?: (resource: ResourceRow) => string;\n /**\n * Host-side hydrate for the resources side of the join. The pack\n * passes the subscription params + ctx; this returns candidate\n * resources. The engine inner-joins on\n * `favorite.resourceId === resource.id`.\n */\n hydrate: (\n params: { actorId: string | undefined; resourceKind?: string },\n ctx: Ctx,\n ) => Iterable<ResourceRow> | Promise<Iterable<ResourceRow>>;\n};\n\nexport type FavoritesPackConfig<\n Ctx = CollectionContext,\n ResourceRow = unknown,\n> = {\n prefix?: string;\n getActorId?: (ctx: Ctx) => string | undefined;\n /** Custom storage adapter. Default per-instance in-memory. */\n store?: FavoritesStore;\n /** Wall-clock. Default `Date.now`. */\n now?: () => number;\n /**\n * Optional join collection. When set, registers\n * `favorites-with-resource` and adds the resource table to\n * `readsTables`.\n */\n joinResources?: JoinResourcesConfig<Ctx, ResourceRow>;\n};\n\nconst resolveActor = (\n getActorId: NonNullable<FavoritesPackConfig[\"getActorId\"]>,\n ctx: unknown,\n): string => {\n const actorId = getActorId(ctx as CollectionContext);\n if (actorId === undefined || actorId === \"\") {\n throw new UnauthorizedError(\"favorites mutation (no actor id)\");\n }\n return actorId;\n};\n\nconst rowId = (\n actorId: string,\n resourceKind: string,\n resourceId: string,\n): string => `${actorId}:${resourceKind}:${resourceId}`;\n\n/**\n * Build a {@link SyncPack} that exposes a per-actor list of favorited\n * host-side resources. Optional join collection pairs each favorite with\n * its host resource row.\n */\nexport const createFavoritesPack = <\n Ctx = CollectionContext,\n ResourceRow = unknown,\n>(\n config: FavoritesPackConfig<Ctx, ResourceRow> = {},\n): SyncPack => {\n const prefix = config.prefix ?? \"\";\n const table = `${prefix}favorites`;\n const collectionName = table;\n const joinCollectionName = `${prefix}favorites-with-resource`;\n const favoriteMutationName = `${prefix}favorites:favorite`;\n const unfavoriteMutationName = `${prefix}favorites:unfavorite`;\n const toggleMutationName = `${prefix}favorites:toggle`;\n const store = config.store ?? createInMemoryFavoritesStore();\n const now = config.now ?? Date.now;\n const getActorId = (config.getActorId ??\n ((ctx: CollectionContext) => (ctx as { userId?: string }).userId)) as (\n ctx: CollectionContext,\n ) => string | undefined;\n const joinResources = config.joinResources;\n const resourceKey = (joinResources?.key ??\n ((row: unknown) => (row as { id: string }).id)) as (\n resource: unknown,\n ) => string;\n\n type Params = { resourceKind?: string };\n\n const pack: SyncPack = {\n name: \"@absolutejs/sync-pack-favorites\",\n ownsTables: [table],\n readsTables: joinResources === undefined ? [] : [joinResources.table],\n version: \"0.2.0\",\n\n schemas: defineSchema({\n [table]: {\n fields: {\n id: field.string,\n actorId: field.string,\n resourceKind: field.string,\n resourceId: field.string,\n createdAt: field.number,\n pinnedAt: (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: FavoriteRow) => {\n const callerId = getActorId(ctx as CollectionContext);\n return callerId !== undefined && row.actorId === callerId;\n },\n insert: (ctx: unknown, row: FavoriteRow) => {\n const callerId = getActorId(ctx as CollectionContext);\n return callerId !== undefined && row.actorId === callerId;\n },\n update: (ctx: unknown, row: FavoriteRow) => {\n const callerId = getActorId(ctx as CollectionContext);\n return callerId !== undefined && row.actorId === callerId;\n },\n delete: (ctx: unknown, row: FavoriteRow) => {\n const callerId = getActorId(ctx as CollectionContext);\n if (callerId === undefined) return false;\n const ownerId =\n (row as { actorId?: string }).actorId ??\n (() => {\n const id = (row as { id?: string }).id;\n return id === undefined ? undefined : store.getById(id)?.actorId;\n })();\n return ownerId === callerId;\n },\n },\n },\n\n collections: [\n defineCollection<FavoriteRow, 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 FavoriteRow[]).filter(\n (row) =>\n row.actorId === callerId &&\n (params.resourceKind === undefined ||\n row.resourceKind === params.resourceKind),\n );\n },\n match: (row, params, ctx) => {\n const callerId = getActorId(ctx);\n if (callerId === undefined || row.actorId !== callerId) return false;\n return (\n params.resourceKind === undefined ||\n row.resourceKind === params.resourceKind\n );\n },\n authorize: (_params, ctx) =>\n getActorId(ctx as CollectionContext) !== undefined,\n }),\n ],\n\n mutations: [\n defineMutation<\n { resourceKind: string; resourceId: string },\n CollectionContext,\n FavoriteRow\n >({\n name: favoriteMutationName,\n handler: async (args, ctx, actions) => {\n const actorId = resolveActor(getActorId, ctx);\n const id = rowId(actorId, args.resourceKind, args.resourceId);\n const existing = store.getById(id);\n if (existing !== undefined) return existing;\n const row: FavoriteRow = {\n actorId,\n createdAt: now(),\n id,\n pinnedAt: null,\n resourceId: args.resourceId,\n resourceKind: args.resourceKind,\n };\n return (await actions.insert(table, row)) as FavoriteRow;\n },\n }),\n defineMutation<\n { resourceKind: string; resourceId: string },\n CollectionContext,\n void\n >({\n name: unfavoriteMutationName,\n handler: async (args, ctx, actions) => {\n const actorId = resolveActor(getActorId, ctx);\n const id = rowId(actorId, args.resourceKind, args.resourceId);\n if (store.getById(id) === undefined) return;\n await actions.delete(table, { id });\n },\n }),\n // Convenience: toggle is one round-trip from the client.\n defineMutation<\n { resourceKind: string; resourceId: string },\n CollectionContext,\n { favorited: boolean }\n >({\n name: toggleMutationName,\n handler: async (args, ctx, actions) => {\n const actorId = resolveActor(getActorId, ctx);\n const id = rowId(actorId, args.resourceKind, args.resourceId);\n const existing = store.getById(id);\n if (existing === undefined) {\n const row: FavoriteRow = {\n actorId,\n createdAt: now(),\n id,\n pinnedAt: null,\n resourceId: args.resourceId,\n resourceKind: args.resourceKind,\n };\n await actions.insert(table, row);\n return { favorited: true };\n }\n await actions.delete(table, { id });\n return { favorited: false };\n },\n }),\n // 0.2: pinning — flips a per-actor `pinnedAt` timestamp so\n // clients can sort pinned-first. Idempotent: pinning twice\n // keeps the original `pinnedAt`; unpinning a non-favorite is\n // a no-op.\n defineMutation<\n { resourceKind: string; resourceId: string },\n CollectionContext,\n FavoriteRow\n >({\n name: `${prefix}favorites:pin`,\n handler: async (args, ctx, actions) => {\n const actorId = resolveActor(getActorId, ctx);\n const id = rowId(actorId, args.resourceKind, args.resourceId);\n const existing = store.getById(id);\n if (existing === undefined) {\n const row: FavoriteRow = {\n actorId,\n createdAt: now(),\n id,\n pinnedAt: now(),\n resourceId: args.resourceId,\n resourceKind: args.resourceKind,\n };\n return (await actions.insert(table, row)) as FavoriteRow;\n }\n if (existing.pinnedAt !== null) return existing;\n return (await actions.update(table, {\n ...existing,\n pinnedAt: now(),\n })) as FavoriteRow;\n },\n }),\n defineMutation<\n { resourceKind: string; resourceId: string },\n CollectionContext,\n FavoriteRow | undefined\n >({\n name: `${prefix}favorites:unpin`,\n handler: async (args, ctx, actions) => {\n const actorId = resolveActor(getActorId, ctx);\n const id = rowId(actorId, args.resourceKind, args.resourceId);\n const existing = store.getById(id);\n if (existing === undefined || existing.pinnedAt === null) {\n return existing;\n }\n return (await actions.update(table, {\n ...existing,\n pinnedAt: null,\n })) as FavoriteRow;\n },\n }),\n defineMutation<\n { resourceKind: string; resourceId: string },\n CollectionContext,\n { pinned: boolean }\n >({\n name: `${prefix}favorites:togglePin`,\n handler: async (args, ctx, actions) => {\n const actorId = resolveActor(getActorId, ctx);\n const id = rowId(actorId, args.resourceKind, args.resourceId);\n const existing = store.getById(id);\n if (existing === undefined) {\n const row: FavoriteRow = {\n actorId,\n createdAt: now(),\n id,\n pinnedAt: now(),\n resourceId: args.resourceId,\n resourceKind: args.resourceKind,\n };\n await actions.insert(table, row);\n return { pinned: true };\n }\n if (existing.pinnedAt === null) {\n await actions.update(table, {\n ...existing,\n pinnedAt: now(),\n });\n return { pinned: true };\n }\n await actions.update(table, {\n ...existing,\n pinnedAt: null,\n });\n return { pinned: false };\n },\n }),\n ],\n };\n\n if (joinResources !== undefined) {\n const resourceHydrate = joinResources.hydrate;\n pack.joinCollections = [\n defineJoinCollection<\n FavoriteRow,\n unknown,\n FavoriteWithResource,\n Params,\n CollectionContext\n >({\n name: joinCollectionName,\n key: (out) => out.id,\n left: {\n table,\n hydrate: (params, ctx) => {\n const callerId = getActorId(ctx);\n if (callerId === undefined) return [];\n return (store.reader.all(ctx) as FavoriteRow[]).filter(\n (row) =>\n row.actorId === callerId &&\n (params.resourceKind === undefined ||\n row.resourceKind === params.resourceKind),\n );\n },\n key: (row) => row.id,\n on: (row) => row.resourceId,\n match: (row, params, ctx) => {\n const callerId = getActorId(ctx);\n if (callerId === undefined || row.actorId !== callerId)\n return false;\n return (\n params.resourceKind === undefined ||\n row.resourceKind === params.resourceKind\n );\n },\n },\n right: {\n table: joinResources.table,\n hydrate: (params, ctx) =>\n resourceHydrate(\n {\n actorId: getActorId(ctx),\n resourceKind: params.resourceKind,\n },\n ctx as Ctx,\n ) as Iterable<unknown>,\n key: (resource) => resourceKey(resource),\n on: (resource) => resourceKey(resource),\n },\n select: (favorite, resource) => ({ ...favorite, resource }),\n authorize: (_params, ctx) =>\n getActorId(ctx as CollectionContext) !== undefined,\n }),\n ];\n }\n\n return defineSyncPack(pack);\n};\n"
5
+ "/**\n * `@absolutejs/sync-pack-favorites` — per-actor saved resources.\n *\n * Owns a `favorites` table; rows are `(actorId, resourceKind, resourceId)`\n * triples with a stable primary key derived from them, so toggling\n * favorite/unfavorite is an upsert/delete with no extra bookkeeping.\n *\n * Each actor sees only their own rows. Optional `joinResources` registers\n * a `favorites-with-resource` join collection that pairs each favorite\n * with the host's resource row (same shape as comments-with-author from\n * the comments pack). Useful when the UI wants \"title + body + favorited?\"\n * in one subscription.\n */\n\nimport {\n\tdefineCollection,\n\tdefineJoinCollection,\n\tdefineMutation,\n\tdefineSchema,\n\tdefineSyncPack,\n\tfield,\n\tUnauthorizedError,\n\ttype CollectionContext,\n\ttype SyncPack,\n\ttype TableReader,\n\ttype TableWriter\n} from '@absolutejs/sync/engine';\n\n/** A single favorited resource for a single actor. */\nexport type FavoriteRow = {\n\t/** `${actorId}:${resourceKind}:${resourceId}` — deterministic so toggling is idempotent. */\n\tid: string;\n\tactorId: string;\n\t/** App-level resource type (\"doc\", \"task\", \"issue\", ...). Lets one inbox span multiple kinds. */\n\tresourceKind: string;\n\tresourceId: string;\n\tcreatedAt: number;\n\t/**\n\t * When the actor pinned this favorite, or `null` when unpinned.\n\t * Clients can sort pinned-first by descending `pinnedAt`. Set by the\n\t * `favorites:pin` / `favorites:togglePin` mutations.\n\t */\n\tpinnedAt: number | null;\n};\n\n/** A `FavoriteRow` paired with the host's resource row, from the optional join. */\nexport type FavoriteWithResource<ResourceRow = unknown> = FavoriteRow & {\n\tresource: ResourceRow;\n};\n\nexport type FavoritesStore = {\n\treader: TableReader<CollectionContext>;\n\twriter: TableWriter<FavoriteRow, CollectionContext, unknown>;\n\tgetById: (id: string) => FavoriteRow | undefined;\n};\n\nexport const createInMemoryFavoritesStore = (): FavoritesStore => {\n\tconst rows = new Map<string, FavoriteRow>();\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 FavoriteRow;\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\nexport type JoinResourcesConfig<Ctx, ResourceRow = unknown> = {\n\t/**\n\t * The host's resource table name. Surfaced in `readsTables` so the\n\t * dependency graph is reviewable. Required when the join is enabled\n\t * (no default — many apps have multiple resource tables).\n\t */\n\ttable: string;\n\t/** Get the host resource's id (the join field). Default `(row) => row.id`. */\n\tkey?: (resource: ResourceRow) => string;\n\t/**\n\t * Host-side hydrate for the resources side of the join. The pack\n\t * passes the subscription params + ctx; this returns candidate\n\t * resources. The engine inner-joins on\n\t * `favorite.resourceId === resource.id`.\n\t */\n\thydrate: (\n\t\tparams: { actorId: string | undefined; resourceKind?: string },\n\t\tctx: Ctx\n\t) => Iterable<ResourceRow> | Promise<Iterable<ResourceRow>>;\n};\n\nexport type FavoritesPackConfig<Ctx = CollectionContext, ResourceRow = unknown> = {\n\tprefix?: string;\n\tgetActorId?: (ctx: Ctx) => string | undefined;\n\t/** Custom storage adapter. Default per-instance in-memory. */\n\tstore?: FavoritesStore;\n\t/** Wall-clock. Default `Date.now`. */\n\tnow?: () => number;\n\t/**\n\t * Optional join collection. When set, registers\n\t * `favorites-with-resource` and adds the resource table to\n\t * `readsTables`.\n\t */\n\tjoinResources?: JoinResourcesConfig<Ctx, ResourceRow>;\n};\n\nconst resolveActor = (\n\tgetActorId: NonNullable<FavoritesPackConfig['getActorId']>,\n\tctx: unknown\n): string => {\n\tconst actorId = getActorId(ctx as CollectionContext);\n\tif (actorId === undefined || actorId === '') {\n\t\tthrow new UnauthorizedError('favorites mutation (no actor id)');\n\t}\n\treturn actorId;\n};\n\nconst rowId = (\n\tactorId: string,\n\tresourceKind: string,\n\tresourceId: string\n): string => `${actorId}:${resourceKind}:${resourceId}`;\n\n/**\n * Build a {@link SyncPack} that exposes a per-actor list of favorited\n * host-side resources. Optional join collection pairs each favorite with\n * its host resource row.\n */\nexport const createFavoritesPack = <\n\tCtx = CollectionContext,\n\tResourceRow = unknown\n>(\n\tconfig: FavoritesPackConfig<Ctx, ResourceRow> = {}\n): SyncPack => {\n\tconst prefix = config.prefix ?? '';\n\tconst table = `${prefix}favorites`;\n\tconst collectionName = table;\n\tconst joinCollectionName = `${prefix}favorites-with-resource`;\n\tconst favoriteMutationName = `${prefix}favorites:favorite`;\n\tconst unfavoriteMutationName = `${prefix}favorites:unfavorite`;\n\tconst toggleMutationName = `${prefix}favorites:toggle`;\n\tconst store = config.store ?? createInMemoryFavoritesStore();\n\tconst now = config.now ?? Date.now;\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 joinResources = config.joinResources;\n\tconst resourceKey = (joinResources?.key ?? ((row: unknown) =>\n\t\t(row as { id: string }).id)) as (resource: unknown) => string;\n\n\ttype Params = { resourceKind?: string };\n\n\tconst pack: SyncPack = {\n\t\tname: '@absolutejs/sync-pack-favorites',\n\t\townsTables: [table],\n\t\treadsTables:\n\t\t\tjoinResources === undefined ? [] : [joinResources.table],\n\t\tversion: '0.2.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\tactorId: field.string,\n\t\t\t\t\tresourceKind: field.string,\n\t\t\t\t\tresourceId: field.string,\n\t\t\t\t\tcreatedAt: field.number,\n\t\t\t\t\tpinnedAt: (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: FavoriteRow) => {\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\tinsert: (ctx: unknown, row: FavoriteRow) => {\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: FavoriteRow) => {\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: FavoriteRow) => {\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 { actorId?: string }).actorId ??\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)?.actorId;\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<FavoriteRow, 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 FavoriteRow[]).filter(\n\t\t\t\t\t\t(row) =>\n\t\t\t\t\t\t\trow.actorId === callerId &&\n\t\t\t\t\t\t\t(params.resourceKind === undefined ||\n\t\t\t\t\t\t\t\trow.resourceKind === params.resourceKind)\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 (callerId === undefined || row.actorId !== callerId)\n\t\t\t\t\t\treturn false;\n\t\t\t\t\treturn (\n\t\t\t\t\t\tparams.resourceKind === undefined ||\n\t\t\t\t\t\trow.resourceKind === params.resourceKind\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\tdefineMutation<\n\t\t\t\t{ resourceKind: string; resourceId: string },\n\t\t\t\tCollectionContext,\n\t\t\t\tFavoriteRow\n\t\t\t>({\n\t\t\t\tname: favoriteMutationName,\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 = rowId(actorId, args.resourceKind, args.resourceId);\n\t\t\t\t\tconst existing = store.getById(id);\n\t\t\t\t\tif (existing !== undefined) return existing;\n\t\t\t\t\tconst row: FavoriteRow = {\n\t\t\t\t\t\tactorId,\n\t\t\t\t\t\tcreatedAt: now(),\n\t\t\t\t\t\tid,\n\t\t\t\t\t\tpinnedAt: null,\n\t\t\t\t\t\tresourceId: args.resourceId,\n\t\t\t\t\t\tresourceKind: args.resourceKind\n\t\t\t\t\t};\n\t\t\t\t\treturn (await actions.insert(table, row)) as FavoriteRow;\n\t\t\t\t}\n\t\t\t}),\n\t\t\tdefineMutation<\n\t\t\t\t{ resourceKind: string; resourceId: string },\n\t\t\t\tCollectionContext,\n\t\t\t\tvoid\n\t\t\t>({\n\t\t\t\tname: unfavoriteMutationName,\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 = rowId(actorId, args.resourceKind, args.resourceId);\n\t\t\t\t\tif (store.getById(id) === undefined) return;\n\t\t\t\t\tawait actions.delete(table, { id });\n\t\t\t\t}\n\t\t\t}),\n\t\t\t// Convenience: toggle is one round-trip from the client.\n\t\t\tdefineMutation<\n\t\t\t\t{ resourceKind: string; resourceId: string },\n\t\t\t\tCollectionContext,\n\t\t\t\t{ favorited: boolean }\n\t\t\t>({\n\t\t\t\tname: toggleMutationName,\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 = rowId(actorId, args.resourceKind, args.resourceId);\n\t\t\t\t\tconst existing = store.getById(id);\n\t\t\t\t\tif (existing === undefined) {\n\t\t\t\t\t\tconst row: FavoriteRow = {\n\t\t\t\t\t\t\tactorId,\n\t\t\t\t\t\t\tcreatedAt: now(),\n\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\tpinnedAt: null,\n\t\t\t\t\t\t\tresourceId: args.resourceId,\n\t\t\t\t\t\t\tresourceKind: args.resourceKind\n\t\t\t\t\t\t};\n\t\t\t\t\t\tawait actions.insert(table, row);\n\t\t\t\t\t\treturn { favorited: true };\n\t\t\t\t\t}\n\t\t\t\t\tawait actions.delete(table, { id });\n\t\t\t\t\treturn { favorited: false };\n\t\t\t\t}\n\t\t\t}),\n\t\t\t// 0.2: pinning — flips a per-actor `pinnedAt` timestamp so\n\t\t\t// clients can sort pinned-first. Idempotent: pinning twice\n\t\t\t// keeps the original `pinnedAt`; unpinning a non-favorite is\n\t\t\t// a no-op.\n\t\t\tdefineMutation<\n\t\t\t\t{ resourceKind: string; resourceId: string },\n\t\t\t\tCollectionContext,\n\t\t\t\tFavoriteRow\n\t\t\t>({\n\t\t\t\tname: `${prefix}favorites:pin`,\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 = rowId(actorId, args.resourceKind, args.resourceId);\n\t\t\t\t\tconst existing = store.getById(id);\n\t\t\t\t\tif (existing === undefined) {\n\t\t\t\t\t\tconst row: FavoriteRow = {\n\t\t\t\t\t\t\tactorId,\n\t\t\t\t\t\t\tcreatedAt: now(),\n\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\tpinnedAt: now(),\n\t\t\t\t\t\t\tresourceId: args.resourceId,\n\t\t\t\t\t\t\tresourceKind: args.resourceKind\n\t\t\t\t\t\t};\n\t\t\t\t\t\treturn (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 FavoriteRow;\n\t\t\t\t\t}\n\t\t\t\t\tif (existing.pinnedAt !== 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\tpinnedAt: now()\n\t\t\t\t\t})) as FavoriteRow;\n\t\t\t\t}\n\t\t\t}),\n\t\t\tdefineMutation<\n\t\t\t\t{ resourceKind: string; resourceId: string },\n\t\t\t\tCollectionContext,\n\t\t\t\tFavoriteRow | undefined\n\t\t\t>({\n\t\t\t\tname: `${prefix}favorites:unpin`,\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 = rowId(actorId, args.resourceKind, args.resourceId);\n\t\t\t\t\tconst existing = store.getById(id);\n\t\t\t\t\tif (existing === undefined || existing.pinnedAt === null) {\n\t\t\t\t\t\treturn existing;\n\t\t\t\t\t}\n\t\t\t\t\treturn (await actions.update(table, {\n\t\t\t\t\t\t...existing,\n\t\t\t\t\t\tpinnedAt: null\n\t\t\t\t\t})) as FavoriteRow;\n\t\t\t\t}\n\t\t\t}),\n\t\t\tdefineMutation<\n\t\t\t\t{ resourceKind: string; resourceId: string },\n\t\t\t\tCollectionContext,\n\t\t\t\t{ pinned: boolean }\n\t\t\t>({\n\t\t\t\tname: `${prefix}favorites:togglePin`,\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 = rowId(actorId, args.resourceKind, args.resourceId);\n\t\t\t\t\tconst existing = store.getById(id);\n\t\t\t\t\tif (existing === undefined) {\n\t\t\t\t\t\tconst row: FavoriteRow = {\n\t\t\t\t\t\t\tactorId,\n\t\t\t\t\t\t\tcreatedAt: now(),\n\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\tpinnedAt: now(),\n\t\t\t\t\t\t\tresourceId: args.resourceId,\n\t\t\t\t\t\t\tresourceKind: args.resourceKind\n\t\t\t\t\t\t};\n\t\t\t\t\t\tawait actions.insert(table, row);\n\t\t\t\t\t\treturn { pinned: true };\n\t\t\t\t\t}\n\t\t\t\t\tif (existing.pinnedAt === null) {\n\t\t\t\t\t\tawait actions.update(table, {\n\t\t\t\t\t\t\t...existing,\n\t\t\t\t\t\t\tpinnedAt: now()\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn { pinned: true };\n\t\t\t\t\t}\n\t\t\t\t\tawait actions.update(table, {\n\t\t\t\t\t\t...existing,\n\t\t\t\t\t\tpinnedAt: null\n\t\t\t\t\t});\n\t\t\t\t\treturn { pinned: false };\n\t\t\t\t}\n\t\t\t})\n\t\t]\n\t};\n\n\tif (joinResources !== undefined) {\n\t\tconst resourceHydrate = joinResources.hydrate;\n\t\tpack.joinCollections = [\n\t\t\tdefineJoinCollection<\n\t\t\t\tFavoriteRow,\n\t\t\t\tunknown,\n\t\t\t\tFavoriteWithResource,\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\tconst callerId = getActorId(ctx);\n\t\t\t\t\t\tif (callerId === undefined) return [];\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\tstore.reader.all(ctx) as FavoriteRow[]\n\t\t\t\t\t\t).filter(\n\t\t\t\t\t\t\t(row) =>\n\t\t\t\t\t\t\t\trow.actorId === callerId &&\n\t\t\t\t\t\t\t\t(params.resourceKind === undefined ||\n\t\t\t\t\t\t\t\t\trow.resourceKind === params.resourceKind)\n\t\t\t\t\t\t);\n\t\t\t\t\t},\n\t\t\t\t\tkey: (row) => row.id,\n\t\t\t\t\ton: (row) => row.resourceId,\n\t\t\t\t\tmatch: (row, params, ctx) => {\n\t\t\t\t\t\tconst callerId = getActorId(ctx);\n\t\t\t\t\t\tif (callerId === undefined || row.actorId !== callerId)\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\tparams.resourceKind === undefined ||\n\t\t\t\t\t\t\trow.resourceKind === params.resourceKind\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tright: {\n\t\t\t\t\ttable: joinResources.table,\n\t\t\t\t\thydrate: (params, ctx) =>\n\t\t\t\t\t\tresourceHydrate(\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tactorId: getActorId(ctx),\n\t\t\t\t\t\t\t\tresourceKind: params.resourceKind\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tctx as Ctx\n\t\t\t\t\t\t) as Iterable<unknown>,\n\t\t\t\t\tkey: (resource) => resourceKey(resource),\n\t\t\t\t\ton: (resource) => resourceKey(resource)\n\t\t\t\t},\n\t\t\t\tselect: (favorite, resource) => ({ ...favorite, resource }),\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\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": "5BEF4F2A607E866D64756E2164756E21",
9
9
  "names": []
10
10
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/sync-pack-favorites",
3
- "version": "0.2.5",
3
+ "version": "0.4.0",
4
4
  "description": "Per-actor saved-resources pack for @absolutejs/sync — favorite/unfavorite/pin/list with optional join to host resources",
5
5
  "repository": {
6
6
  "type": "git",
@@ -38,7 +38,7 @@
38
38
  "@absolutejs/sync": ">=2.11.0 <3"
39
39
  },
40
40
  "devDependencies": {
41
- "@absolutejs/sync": "2.11.0",
41
+ "@absolutejs/sync": "2.29.0",
42
42
  "@types/bun": "1.3.14",
43
43
  "prettier": "3.5.3",
44
44
  "typescript": "5.8.3"
@@ -61,7 +61,53 @@
61
61
  "README.md"
62
62
  ],
63
63
  "absolutejs": {
64
- "manifestContract": 2
64
+ "manifestContract": 2,
65
+ "sync": {
66
+ "localSchema": {
67
+ "version": 1,
68
+ "localData": {
69
+ "collections": [
70
+ {
71
+ "match": "*favorites*",
72
+ "sensitivity": "private",
73
+ "protection": "required",
74
+ "onProtectionUnavailable": "memory-only",
75
+ "evictionPriority": "critical"
76
+ }
77
+ ],
78
+ "mutations": [
79
+ {
80
+ "match": "*favorites*",
81
+ "sensitivity": "private",
82
+ "protection": "required",
83
+ "onProtectionUnavailable": "memory-only",
84
+ "conflict": {
85
+ "strategy": "client-wins",
86
+ "maxAttempts": 1
87
+ }
88
+ },
89
+ {
90
+ "match": "*favorites:toggle",
91
+ "sensitivity": "private",
92
+ "protection": "required",
93
+ "onProtectionUnavailable": "memory-only",
94
+ "conflict": {
95
+ "strategy": "manual"
96
+ }
97
+ },
98
+ {
99
+ "match": "*favorites:togglePin",
100
+ "sensitivity": "private",
101
+ "protection": "required",
102
+ "onProtectionUnavailable": "memory-only",
103
+ "conflict": {
104
+ "strategy": "manual"
105
+ }
106
+ }
107
+ ]
108
+ }
109
+ }
110
+ }
65
111
  },
66
112
  "dependencies": {
67
113
  "@absolutejs/manifest": "^0.9.0",