@absolutejs/sync-pack-favorites 0.1.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/README.md +128 -0
- package/dist/index.d.ts +74 -0
- package/dist/index.js +218 -0
- package/dist/index.js.map +10 -0
- package/package.json +50 -0
package/README.md
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# @absolutejs/sync-pack-favorites
|
|
2
|
+
|
|
3
|
+
Per-actor saved-resources pack for
|
|
4
|
+
[`@absolutejs/sync`](https://github.com/absolutejs/sync). Each actor sees
|
|
5
|
+
only their own rows; favoriting is idempotent (deterministic row id);
|
|
6
|
+
optional join collection pairs each favorite with the host's resource
|
|
7
|
+
row in one live subscription.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
bun add @absolutejs/sync-pack-favorites
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { createSyncEngine } from '@absolutejs/sync/engine';
|
|
17
|
+
import { createFavoritesPack } from '@absolutejs/sync-pack-favorites';
|
|
18
|
+
|
|
19
|
+
const engine = createSyncEngine();
|
|
20
|
+
engine.registerPack(
|
|
21
|
+
createFavoritesPack({
|
|
22
|
+
getActorId: (ctx) => ctx.session.userId,
|
|
23
|
+
}),
|
|
24
|
+
);
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Then from the client:
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
// Toggle is the easy default — one round-trip, returns the new state.
|
|
31
|
+
const { favorited } = await store.mutate({
|
|
32
|
+
args: { resourceKind: 'doc', resourceId: 'doc-123' },
|
|
33
|
+
name: 'favorites:toggle',
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Or favorite / unfavorite explicitly.
|
|
37
|
+
await store.mutate({ args: { resourceKind: 'doc', resourceId: 'doc-123' }, name: 'favorites:favorite' });
|
|
38
|
+
await store.mutate({ args: { resourceKind: 'doc', resourceId: 'doc-123' }, name: 'favorites:unfavorite' });
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## The pack exposes
|
|
42
|
+
|
|
43
|
+
| Surface | Name | What it does |
|
|
44
|
+
| ----------- | ----------------------------- | ----------------------------------------------------------- |
|
|
45
|
+
| Collection | `favorites` | Per-actor list. Optional `params.resourceKind` filter |
|
|
46
|
+
| Mutation | `favorites:favorite` | Idempotent insert. Subsequent calls are no-ops |
|
|
47
|
+
| Mutation | `favorites:unfavorite` | Idempotent delete. No-op if the row never existed |
|
|
48
|
+
| Mutation | `favorites:toggle` | Insert if missing, delete if present. Returns `{ favorited }` |
|
|
49
|
+
| Collection | `favorites-with-resource` (opt) | Join with the host's resource table — see below |
|
|
50
|
+
|
|
51
|
+
## Row shape
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
type FavoriteRow = {
|
|
55
|
+
id: string; // `${actorId}:${resourceKind}:${resourceId}` — deterministic
|
|
56
|
+
actorId: string;
|
|
57
|
+
resourceKind: string; // app-level: "doc" | "task" | "issue" | ...
|
|
58
|
+
resourceId: string;
|
|
59
|
+
createdAt: number;
|
|
60
|
+
};
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The deterministic id means the same `(actor, kind, resource)` triple
|
|
64
|
+
always maps to the same row — so duplicate `favorite` calls are
|
|
65
|
+
idempotent at the storage layer, not just behaviorally.
|
|
66
|
+
|
|
67
|
+
## Optional: `favorites-with-resource` join
|
|
68
|
+
|
|
69
|
+
When you set `joinResources`, the pack additionally registers a join
|
|
70
|
+
collection that pairs each favorite with the host's resource row (same
|
|
71
|
+
pattern as comments-with-author from sync-pack-comments).
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
type DocRow = { id: string; title: string };
|
|
75
|
+
|
|
76
|
+
engine.registerReader('docs', { all: () => db.docs.list() });
|
|
77
|
+
engine.registerPack(
|
|
78
|
+
createFavoritesPack<MyCtx, DocRow>({
|
|
79
|
+
getActorId,
|
|
80
|
+
joinResources: {
|
|
81
|
+
table: 'docs',
|
|
82
|
+
// Default (row) => row.id; override if your resource id key isn't `id`.
|
|
83
|
+
// key: (doc) => doc.docId,
|
|
84
|
+
// Required: host supplies the resource-side hydrate.
|
|
85
|
+
hydrate: () => db.docs.list(),
|
|
86
|
+
},
|
|
87
|
+
}),
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const view = await engine.subscribe<
|
|
91
|
+
FavoriteRow & { resource: DocRow },
|
|
92
|
+
{ resourceKind?: string }
|
|
93
|
+
>({
|
|
94
|
+
collection: 'favorites-with-resource',
|
|
95
|
+
params: { resourceKind: 'doc' },
|
|
96
|
+
ctx,
|
|
97
|
+
onDiff: rerender,
|
|
98
|
+
});
|
|
99
|
+
// view.initial[0] === { ...favorite, resource: { id, title } }
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Inner join — favorites whose resource has been deleted drop out of the
|
|
103
|
+
join (but stay in the base `favorites` collection).
|
|
104
|
+
|
|
105
|
+
## Storage
|
|
106
|
+
|
|
107
|
+
Default: per-instance in-memory store. For a persistent backend, pass a
|
|
108
|
+
custom `store`:
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
import { type FavoritesStore } from '@absolutejs/sync-pack-favorites';
|
|
112
|
+
|
|
113
|
+
const store: FavoritesStore = {
|
|
114
|
+
getById: (id) => /* SELECT * FROM favorites WHERE id = $1 */,
|
|
115
|
+
reader: { all: () => /* SELECT */ },
|
|
116
|
+
writer: { insert, update, delete },
|
|
117
|
+
};
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Multiple instances
|
|
121
|
+
|
|
122
|
+
Pass a `prefix` to coexist with another favorites pack instance (e.g. a
|
|
123
|
+
"team" set vs a "private" set):
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
engine.registerPack(createFavoritesPack({ prefix: 'team_', /* ... */ }));
|
|
127
|
+
engine.registerPack(createFavoritesPack({ prefix: 'private_', /* ... */ }));
|
|
128
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@absolutejs/sync-pack-favorites` — per-actor saved resources.
|
|
3
|
+
*
|
|
4
|
+
* Owns a `favorites` table; rows are `(actorId, resourceKind, resourceId)`
|
|
5
|
+
* triples with a stable primary key derived from them, so toggling
|
|
6
|
+
* favorite/unfavorite is an upsert/delete with no extra bookkeeping.
|
|
7
|
+
*
|
|
8
|
+
* Each actor sees only their own rows. Optional `joinResources` registers
|
|
9
|
+
* a `favorites-with-resource` join collection that pairs each favorite
|
|
10
|
+
* with the host's resource row (same shape as comments-with-author from
|
|
11
|
+
* the comments pack). Useful when the UI wants "title + body + favorited?"
|
|
12
|
+
* in one subscription.
|
|
13
|
+
*/
|
|
14
|
+
import { type CollectionContext, type SyncPack, type TableReader, type TableWriter } from '@absolutejs/sync/engine';
|
|
15
|
+
/** A single favorited resource for a single actor. */
|
|
16
|
+
export type FavoriteRow = {
|
|
17
|
+
/** `${actorId}:${resourceKind}:${resourceId}` — deterministic so toggling is idempotent. */
|
|
18
|
+
id: string;
|
|
19
|
+
actorId: string;
|
|
20
|
+
/** App-level resource type ("doc", "task", "issue", ...). Lets one inbox span multiple kinds. */
|
|
21
|
+
resourceKind: string;
|
|
22
|
+
resourceId: string;
|
|
23
|
+
createdAt: number;
|
|
24
|
+
};
|
|
25
|
+
/** A `FavoriteRow` paired with the host's resource row, from the optional join. */
|
|
26
|
+
export type FavoriteWithResource<ResourceRow = unknown> = FavoriteRow & {
|
|
27
|
+
resource: ResourceRow;
|
|
28
|
+
};
|
|
29
|
+
export type FavoritesStore = {
|
|
30
|
+
reader: TableReader<CollectionContext>;
|
|
31
|
+
writer: TableWriter<FavoriteRow, CollectionContext, unknown>;
|
|
32
|
+
getById: (id: string) => FavoriteRow | undefined;
|
|
33
|
+
};
|
|
34
|
+
export declare const createInMemoryFavoritesStore: () => FavoritesStore;
|
|
35
|
+
export type JoinResourcesConfig<Ctx, ResourceRow = unknown> = {
|
|
36
|
+
/**
|
|
37
|
+
* The host's resource table name. Surfaced in `readsTables` so the
|
|
38
|
+
* dependency graph is reviewable. Required when the join is enabled
|
|
39
|
+
* (no default — many apps have multiple resource tables).
|
|
40
|
+
*/
|
|
41
|
+
table: string;
|
|
42
|
+
/** Get the host resource's id (the join field). Default `(row) => row.id`. */
|
|
43
|
+
key?: (resource: ResourceRow) => string;
|
|
44
|
+
/**
|
|
45
|
+
* Host-side hydrate for the resources side of the join. The pack
|
|
46
|
+
* passes the subscription params + ctx; this returns candidate
|
|
47
|
+
* resources. The engine inner-joins on
|
|
48
|
+
* `favorite.resourceId === resource.id`.
|
|
49
|
+
*/
|
|
50
|
+
hydrate: (params: {
|
|
51
|
+
actorId: string | undefined;
|
|
52
|
+
resourceKind?: string;
|
|
53
|
+
}, ctx: Ctx) => Iterable<ResourceRow> | Promise<Iterable<ResourceRow>>;
|
|
54
|
+
};
|
|
55
|
+
export type FavoritesPackConfig<Ctx = CollectionContext, ResourceRow = unknown> = {
|
|
56
|
+
prefix?: string;
|
|
57
|
+
getActorId?: (ctx: Ctx) => string | undefined;
|
|
58
|
+
/** Custom storage adapter. Default per-instance in-memory. */
|
|
59
|
+
store?: FavoritesStore;
|
|
60
|
+
/** Wall-clock. Default `Date.now`. */
|
|
61
|
+
now?: () => number;
|
|
62
|
+
/**
|
|
63
|
+
* Optional join collection. When set, registers
|
|
64
|
+
* `favorites-with-resource` and adds the resource table to
|
|
65
|
+
* `readsTables`.
|
|
66
|
+
*/
|
|
67
|
+
joinResources?: JoinResourcesConfig<Ctx, ResourceRow>;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Build a {@link SyncPack} that exposes a per-actor list of favorited
|
|
71
|
+
* host-side resources. Optional join collection pairs each favorite with
|
|
72
|
+
* its host resource row.
|
|
73
|
+
*/
|
|
74
|
+
export declare const createFavoritesPack: <Ctx = CollectionContext, ResourceRow = unknown>(config?: FavoritesPackConfig<Ctx, ResourceRow>) => SyncPack;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/index.ts
|
|
3
|
+
import {
|
|
4
|
+
defineCollection,
|
|
5
|
+
defineJoinCollection,
|
|
6
|
+
defineMutation,
|
|
7
|
+
defineSchema,
|
|
8
|
+
defineSyncPack,
|
|
9
|
+
field,
|
|
10
|
+
UnauthorizedError
|
|
11
|
+
} from "@absolutejs/sync/engine";
|
|
12
|
+
var createInMemoryFavoritesStore = () => {
|
|
13
|
+
const rows = new Map;
|
|
14
|
+
return {
|
|
15
|
+
getById: (id) => rows.get(id),
|
|
16
|
+
reader: {
|
|
17
|
+
all: () => [...rows.values()]
|
|
18
|
+
},
|
|
19
|
+
writer: {
|
|
20
|
+
delete: (row) => {
|
|
21
|
+
rows.delete(row.id);
|
|
22
|
+
},
|
|
23
|
+
insert: (data) => {
|
|
24
|
+
rows.set(data.id, data);
|
|
25
|
+
return data;
|
|
26
|
+
},
|
|
27
|
+
update: (data) => {
|
|
28
|
+
const prior = rows.get(data.id);
|
|
29
|
+
const merged = { ...prior ?? {}, ...data };
|
|
30
|
+
rows.set(data.id, merged);
|
|
31
|
+
return merged;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
var resolveActor = (getActorId, ctx) => {
|
|
37
|
+
const actorId = getActorId(ctx);
|
|
38
|
+
if (actorId === undefined || actorId === "") {
|
|
39
|
+
throw new UnauthorizedError("favorites mutation (no actor id)");
|
|
40
|
+
}
|
|
41
|
+
return actorId;
|
|
42
|
+
};
|
|
43
|
+
var rowId = (actorId, resourceKind, resourceId) => `${actorId}:${resourceKind}:${resourceId}`;
|
|
44
|
+
var createFavoritesPack = (config = {}) => {
|
|
45
|
+
const prefix = config.prefix ?? "";
|
|
46
|
+
const table = `${prefix}favorites`;
|
|
47
|
+
const collectionName = table;
|
|
48
|
+
const joinCollectionName = `${prefix}favorites-with-resource`;
|
|
49
|
+
const favoriteMutationName = `${prefix}favorites:favorite`;
|
|
50
|
+
const unfavoriteMutationName = `${prefix}favorites:unfavorite`;
|
|
51
|
+
const toggleMutationName = `${prefix}favorites:toggle`;
|
|
52
|
+
const store = config.store ?? createInMemoryFavoritesStore();
|
|
53
|
+
const now = config.now ?? Date.now;
|
|
54
|
+
const getActorId = config.getActorId ?? ((ctx) => ctx.userId);
|
|
55
|
+
const joinResources = config.joinResources;
|
|
56
|
+
const resourceKey = joinResources?.key ?? ((row) => row.id);
|
|
57
|
+
const pack = {
|
|
58
|
+
name: "@absolutejs/sync-pack-favorites",
|
|
59
|
+
ownsTables: [table],
|
|
60
|
+
readsTables: joinResources === undefined ? [] : [joinResources.table],
|
|
61
|
+
version: "0.1.0",
|
|
62
|
+
schemas: defineSchema({
|
|
63
|
+
[table]: {
|
|
64
|
+
fields: {
|
|
65
|
+
id: field.string,
|
|
66
|
+
actorId: field.string,
|
|
67
|
+
resourceKind: field.string,
|
|
68
|
+
resourceId: field.string,
|
|
69
|
+
createdAt: field.number
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}),
|
|
73
|
+
readers: { [table]: store.reader },
|
|
74
|
+
writers: { [table]: store.writer },
|
|
75
|
+
permissions: {
|
|
76
|
+
[table]: {
|
|
77
|
+
read: (ctx, row) => {
|
|
78
|
+
const callerId = getActorId(ctx);
|
|
79
|
+
return callerId !== undefined && row.actorId === callerId;
|
|
80
|
+
},
|
|
81
|
+
insert: (ctx, row) => {
|
|
82
|
+
const callerId = getActorId(ctx);
|
|
83
|
+
return callerId !== undefined && row.actorId === callerId;
|
|
84
|
+
},
|
|
85
|
+
update: (ctx, row) => {
|
|
86
|
+
const callerId = getActorId(ctx);
|
|
87
|
+
return callerId !== undefined && row.actorId === callerId;
|
|
88
|
+
},
|
|
89
|
+
delete: (ctx, row) => {
|
|
90
|
+
const callerId = getActorId(ctx);
|
|
91
|
+
if (callerId === undefined)
|
|
92
|
+
return false;
|
|
93
|
+
const ownerId = row.actorId ?? (() => {
|
|
94
|
+
const id = row.id;
|
|
95
|
+
return id === undefined ? undefined : store.getById(id)?.actorId;
|
|
96
|
+
})();
|
|
97
|
+
return ownerId === callerId;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
collections: [
|
|
102
|
+
defineCollection({
|
|
103
|
+
name: collectionName,
|
|
104
|
+
tables: [table],
|
|
105
|
+
key: (row) => row.id,
|
|
106
|
+
hydrate: (params, ctx) => {
|
|
107
|
+
const callerId = getActorId(ctx);
|
|
108
|
+
if (callerId === undefined)
|
|
109
|
+
return [];
|
|
110
|
+
return store.reader.all(ctx).filter((row) => row.actorId === callerId && (params.resourceKind === undefined || row.resourceKind === params.resourceKind));
|
|
111
|
+
},
|
|
112
|
+
match: (row, params, ctx) => {
|
|
113
|
+
const callerId = getActorId(ctx);
|
|
114
|
+
if (callerId === undefined || row.actorId !== callerId)
|
|
115
|
+
return false;
|
|
116
|
+
return params.resourceKind === undefined || row.resourceKind === params.resourceKind;
|
|
117
|
+
},
|
|
118
|
+
authorize: (_params, ctx) => getActorId(ctx) !== undefined
|
|
119
|
+
})
|
|
120
|
+
],
|
|
121
|
+
mutations: [
|
|
122
|
+
defineMutation({
|
|
123
|
+
name: favoriteMutationName,
|
|
124
|
+
handler: async (args, ctx, actions) => {
|
|
125
|
+
const actorId = resolveActor(getActorId, ctx);
|
|
126
|
+
const id = rowId(actorId, args.resourceKind, args.resourceId);
|
|
127
|
+
const existing = store.getById(id);
|
|
128
|
+
if (existing !== undefined)
|
|
129
|
+
return existing;
|
|
130
|
+
const row = {
|
|
131
|
+
actorId,
|
|
132
|
+
createdAt: now(),
|
|
133
|
+
id,
|
|
134
|
+
resourceId: args.resourceId,
|
|
135
|
+
resourceKind: args.resourceKind
|
|
136
|
+
};
|
|
137
|
+
return await actions.insert(table, row);
|
|
138
|
+
}
|
|
139
|
+
}),
|
|
140
|
+
defineMutation({
|
|
141
|
+
name: unfavoriteMutationName,
|
|
142
|
+
handler: async (args, ctx, actions) => {
|
|
143
|
+
const actorId = resolveActor(getActorId, ctx);
|
|
144
|
+
const id = rowId(actorId, args.resourceKind, args.resourceId);
|
|
145
|
+
if (store.getById(id) === undefined)
|
|
146
|
+
return;
|
|
147
|
+
await actions.delete(table, { id });
|
|
148
|
+
}
|
|
149
|
+
}),
|
|
150
|
+
defineMutation({
|
|
151
|
+
name: toggleMutationName,
|
|
152
|
+
handler: async (args, ctx, actions) => {
|
|
153
|
+
const actorId = resolveActor(getActorId, ctx);
|
|
154
|
+
const id = rowId(actorId, args.resourceKind, args.resourceId);
|
|
155
|
+
const existing = store.getById(id);
|
|
156
|
+
if (existing === undefined) {
|
|
157
|
+
const row = {
|
|
158
|
+
actorId,
|
|
159
|
+
createdAt: now(),
|
|
160
|
+
id,
|
|
161
|
+
resourceId: args.resourceId,
|
|
162
|
+
resourceKind: args.resourceKind
|
|
163
|
+
};
|
|
164
|
+
await actions.insert(table, row);
|
|
165
|
+
return { favorited: true };
|
|
166
|
+
}
|
|
167
|
+
await actions.delete(table, { id });
|
|
168
|
+
return { favorited: false };
|
|
169
|
+
}
|
|
170
|
+
})
|
|
171
|
+
]
|
|
172
|
+
};
|
|
173
|
+
if (joinResources !== undefined) {
|
|
174
|
+
const resourceHydrate = joinResources.hydrate;
|
|
175
|
+
pack.joinCollections = [
|
|
176
|
+
defineJoinCollection({
|
|
177
|
+
name: joinCollectionName,
|
|
178
|
+
key: (out) => out.id,
|
|
179
|
+
left: {
|
|
180
|
+
table,
|
|
181
|
+
hydrate: (params, ctx) => {
|
|
182
|
+
const callerId = getActorId(ctx);
|
|
183
|
+
if (callerId === undefined)
|
|
184
|
+
return [];
|
|
185
|
+
return store.reader.all(ctx).filter((row) => row.actorId === callerId && (params.resourceKind === undefined || row.resourceKind === params.resourceKind));
|
|
186
|
+
},
|
|
187
|
+
key: (row) => row.id,
|
|
188
|
+
on: (row) => row.resourceId,
|
|
189
|
+
match: (row, params, ctx) => {
|
|
190
|
+
const callerId = getActorId(ctx);
|
|
191
|
+
if (callerId === undefined || row.actorId !== callerId)
|
|
192
|
+
return false;
|
|
193
|
+
return params.resourceKind === undefined || row.resourceKind === params.resourceKind;
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
right: {
|
|
197
|
+
table: joinResources.table,
|
|
198
|
+
hydrate: (params, ctx) => resourceHydrate({
|
|
199
|
+
actorId: getActorId(ctx),
|
|
200
|
+
resourceKind: params.resourceKind
|
|
201
|
+
}, ctx),
|
|
202
|
+
key: (resource) => resourceKey(resource),
|
|
203
|
+
on: (resource) => resourceKey(resource)
|
|
204
|
+
},
|
|
205
|
+
select: (favorite, resource) => ({ ...favorite, resource }),
|
|
206
|
+
authorize: (_params, ctx) => getActorId(ctx) !== undefined
|
|
207
|
+
})
|
|
208
|
+
];
|
|
209
|
+
}
|
|
210
|
+
return defineSyncPack(pack);
|
|
211
|
+
};
|
|
212
|
+
export {
|
|
213
|
+
createInMemoryFavoritesStore,
|
|
214
|
+
createFavoritesPack
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
//# debugId=E27906193B10A86564756E2164756E21
|
|
218
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.ts"],
|
|
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\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};\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.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\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}\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\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\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]\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
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "E27906193B10A86564756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@absolutejs/sync-pack-favorites",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Per-actor saved-resources pack for @absolutejs/sync — favorite/unfavorite/list with optional join to host resources",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/absolutejs/sync-packs.git",
|
|
8
|
+
"directory": "favorites"
|
|
9
|
+
},
|
|
10
|
+
"main": "./dist/index.js",
|
|
11
|
+
"module": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"type": "module",
|
|
14
|
+
"license": "CC BY-NC 4.0",
|
|
15
|
+
"author": "Alex Kahn",
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"absolutejs",
|
|
21
|
+
"sync",
|
|
22
|
+
"sync-pack",
|
|
23
|
+
"favorites",
|
|
24
|
+
"bookmarks"
|
|
25
|
+
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "rm -rf dist && bun build src/index.ts --outdir dist --sourcemap --target=bun --external @absolutejs/sync && tsc --project tsconfig.build.json",
|
|
28
|
+
"test": "bun test",
|
|
29
|
+
"typecheck": "tsc --noEmit",
|
|
30
|
+
"format": "prettier --write \"./**/*.{ts,json,md}\"",
|
|
31
|
+
"release": "bun run format && bun run typecheck && bun run test && bun run build && bun publish"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"@absolutejs/sync": ">= 1.9.2"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@absolutejs/sync": "^1.9.2",
|
|
38
|
+
"@types/bun": "1.3.14",
|
|
39
|
+
"prettier": "3.5.3",
|
|
40
|
+
"typescript": "5.8.3"
|
|
41
|
+
},
|
|
42
|
+
"exports": {
|
|
43
|
+
".": {
|
|
44
|
+
"types": "./dist/index.d.ts",
|
|
45
|
+
"import": "./dist/index.js",
|
|
46
|
+
"default": "./dist/index.js"
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"files": ["dist", "README.md"]
|
|
50
|
+
}
|