@rocksky/sdk 0.12.0 → 0.14.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 +27 -2
- package/dist/client.d.ts +33 -9
- package/dist/client.d.ts.map +1 -1
- package/dist/dedup.js +215 -0
- package/dist/errors.d.ts +5 -2
- package/dist/errors.d.ts.map +1 -1
- package/dist/filter.d.ts +122 -0
- package/dist/filter.d.ts.map +1 -0
- package/dist/filter.test.d.ts +2 -0
- package/dist/filter.test.d.ts.map +1 -0
- package/dist/generated/types.d.ts +20 -0
- package/dist/generated/types.d.ts.map +1 -1
- package/dist/hash.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +204 -213
- package/dist/remote.d.ts.map +1 -1
- package/package.json +8 -2
- package/src/agent.ts +3 -3
- package/src/client.ts +91 -10
- package/src/errors.ts +7 -2
- package/src/filter.test.ts +94 -0
- package/src/filter.ts +192 -0
- package/src/generated/types.ts +21 -0
- package/src/hash.ts +4 -2
- package/src/index.ts +11 -1
- package/src/library.ts +2 -2
- package/src/remote.ts +4 -5
package/README.md
CHANGED
|
@@ -45,6 +45,29 @@ methods cover the rest: `album`, `artist`, `song`, `feed`, `playlists`,
|
|
|
45
45
|
Every named method is sugar over the universal escape hatch **`rk.get(nsid,
|
|
46
46
|
params)`**, which calls ANY read query by nsid and returns `unknown`.
|
|
47
47
|
|
|
48
|
+
**Filtering**: `catalogSongs`, `catalogArtists`, `catalogAlbums`, and
|
|
49
|
+
`scrobbleFeed` take an optional RSQL `filter` (string or `Filter`) — build one
|
|
50
|
+
fluently, with the per-endpoint field constants for discoverability:
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import { Filter, RockskyClient, ScrobbleFields, SongFields } from "@rocksky/sdk";
|
|
54
|
+
|
|
55
|
+
const rk = new RockskyClient();
|
|
56
|
+
const filter = Filter.eq(SongFields.artist, "Daft Punk")
|
|
57
|
+
.and(Filter.gt(SongFields.duration, 200_000))
|
|
58
|
+
.or(Filter.in(SongFields.genre, ["house", "electro"]));
|
|
59
|
+
const songs = await rk.catalogSongs(50, 0, undefined, filter);
|
|
60
|
+
// filter sent: artist=="Daft Punk";duration=gt=200000,genre=in=(house,electro)
|
|
61
|
+
|
|
62
|
+
// scrobbles support dotted selectors into the joined track/user/artist:
|
|
63
|
+
await rk.scrobbleFeed(undefined, false, 50, 0,
|
|
64
|
+
Filter.eq(ScrobbleFields.trackArtist, "Daft Punk").and(Filter.ge(ScrobbleFields.date, "2025-01-01")));
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Operators: `eq` (supports `*` wildcards), `ne`, `gt`, `ge`, `lt`, `le`, `in`,
|
|
68
|
+
`out`, `isNull`, `isNotNull`, chained with `.and()` / `.or()` (parentheses
|
|
69
|
+
inserted automatically per RSQL precedence).
|
|
70
|
+
|
|
48
71
|
**Typed date-window charts**: `topTracksInterval(limit, offset, interval)` and
|
|
49
72
|
`topArtistsInterval(...)` take a `DateInterval` built with the `Interval`
|
|
50
73
|
factories — `Interval.allTime()`, `Interval.lastDays(n)`, `Interval.lastWeeks(n)`,
|
|
@@ -86,10 +109,12 @@ SHA-256, identical to the server and every other Rocksky SDK.
|
|
|
86
109
|
## Duplicate prevention + real-time sync
|
|
87
110
|
|
|
88
111
|
An optional local index (embedded [classic-level](https://github.com/Level/classic-level)
|
|
89
|
-
LevelDB) prevents duplicate writes and stays live off the firehose
|
|
112
|
+
LevelDB) prevents duplicate writes and stays live off the firehose. It is
|
|
113
|
+
Node-only, so it lives on its own subpath — the main `@rocksky/sdk` entry stays
|
|
114
|
+
browser-safe:
|
|
90
115
|
|
|
91
116
|
```ts
|
|
92
|
-
import { RockskyIndex } from "@rocksky/sdk";
|
|
117
|
+
import { RockskyIndex } from "@rocksky/sdk/dedup";
|
|
93
118
|
|
|
94
119
|
const idx = new RockskyIndex("./dedup");
|
|
95
120
|
await idx.open();
|
package/dist/client.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import type { Filter } from "./filter.js";
|
|
1
2
|
import { RockskyLibrary } from "./library.js";
|
|
2
|
-
import type { ActorProfileViewBasic, ActorProfileViewDetailed, AlbumViewBasic, AlbumViewDetailed, ArtistViewBasic, ArtistViewDetailed, ChartsView, FeedGeneratorsView, FeedRecommendationsView, FeedRecommendedAlbumsView, FeedRecommendedArtistsView, FeedSearchResultsView, FeedStoriesView, FeedView, GetActorCompatibilityOutput, GetActorNeighboursOutput, GetActorPlaylistsOutput, GetAlbumShoutsOutput, GetApikeysOutput, GetArtistListenersOutput, GetArtistRecentListenersOutput, GetArtistShoutsOutput, GetFeedGeneratorOutput, GetMirrorSourcesOutput, GetProfileShoutsOutput, GetShoutRepliesOutput, GetSongRecentListenersOutput, GetTrackShoutsOutput, GetUnreadCountOutput, ListNotificationsOutput, PlayerCurrentlyPlayingViewDetailed, PlayerPlaybackQueueViewDetailed, PlaylistGetPlaylistsOutput, PlaylistViewDetailed, RockboxSettingsView, ScrobbleViewBasic, SongViewBasic, SongViewDetailed, StatsGlobalStatsView, StatsView, StatsWrappedView, UpdateSeenOutput } from "./generated/types.js";
|
|
3
|
+
import type { ActorProfileViewBasic, ActorProfileViewDetailed, AlbumViewBasic, AlbumViewDetailed, ArtistViewBasic, ArtistViewDetailed, ChartsView, CreateScrobbleInput, FollowAccountOutput, FeedGeneratorsView, FeedRecommendationsView, FeedRecommendedAlbumsView, FeedRecommendedArtistsView, FeedSearchResultsView, FeedStoriesView, FeedView, GetActorCompatibilityOutput, GetActorNeighboursOutput, GetActorPlaylistsOutput, GetAlbumShoutsOutput, GetApikeysOutput, GetArtistListenersOutput, GetArtistRecentListenersOutput, GetArtistShoutsOutput, GetFeedGeneratorOutput, GetMirrorSourcesOutput, GetProfileShoutsOutput, GetShoutRepliesOutput, GetSongRecentListenersOutput, GetTrackShoutsOutput, GetUnreadCountOutput, ListNotificationsOutput, MirrorSourceView, PlayerCurrentlyPlayingViewDetailed, PlayerPlaybackQueueViewDetailed, PlaylistGetPlaylistsOutput, PlaylistViewDetailed, PutAudioSettingsInput, PutMirrorSourceInput, RockboxSettingsView, ScrobbleViewBasic, SongViewBasic, SongViewDetailed, StatsGlobalStatsView, StatsView, StatsWrappedView, UnfollowAccountOutput, UpdateSeenOutput } from "./generated/types.js";
|
|
3
4
|
/**
|
|
4
5
|
* A typed date window for the `top*` charts. Build one with the {@link Interval}
|
|
5
6
|
* factories; `range` bounds are RFC-3339 datetimes.
|
|
@@ -50,6 +51,13 @@ export declare class RockskyClient {
|
|
|
50
51
|
/** Call any AppView read query by nsid; returns the raw JSON response. Every
|
|
51
52
|
* method here is sugar over this — use it for queries without a wrapper. */
|
|
52
53
|
get(nsid: string, params?: Record<string, unknown>): Promise<unknown>;
|
|
54
|
+
/** Call any AppView procedure by nsid. `params` ride the query string (some
|
|
55
|
+
* procedures take their arguments there), `body` is the JSON input — omitted
|
|
56
|
+
* entirely when `undefined`. Escape hatch for procedures without a wrapper. */
|
|
57
|
+
post<T = unknown>(nsid: string, opts?: {
|
|
58
|
+
params?: Record<string, unknown>;
|
|
59
|
+
body?: unknown;
|
|
60
|
+
}): Promise<T>;
|
|
53
61
|
/** An actor's most-played songs. */
|
|
54
62
|
songs(actor: string, limit?: number, offset?: number): Promise<SongViewBasic[]>;
|
|
55
63
|
/** An actor's loved (liked) songs. */
|
|
@@ -66,12 +74,15 @@ export declare class RockskyClient {
|
|
|
66
74
|
topTracksInterval(limit: number, offset: number, interval: DateInterval): Promise<SongViewBasic[]>;
|
|
67
75
|
/** The top artists chart over a typed {@link DateInterval}. */
|
|
68
76
|
topArtistsInterval(limit: number, offset: number, interval: DateInterval): Promise<ArtistViewBasic[]>;
|
|
69
|
-
/** The album catalog, optionally filtered by `genre
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
77
|
+
/** The album catalog, optionally filtered by `genre` and/or an RSQL
|
|
78
|
+
* {@link Filter} expression (see {@link AlbumFields}). */
|
|
79
|
+
catalogAlbums(limit?: number, offset?: number, genre?: string, filter?: string | Filter): Promise<AlbumViewBasic[]>;
|
|
80
|
+
/** The artist catalog, optionally filtered by `genre` and/or an RSQL
|
|
81
|
+
* {@link Filter} expression (see {@link ArtistFields}). */
|
|
82
|
+
catalogArtists(limit?: number, offset?: number, genre?: string, filter?: string | Filter): Promise<ArtistViewBasic[]>;
|
|
83
|
+
/** The song catalog, optionally filtered by `genre` and/or an RSQL
|
|
84
|
+
* {@link Filter} expression (see {@link SongFields}). */
|
|
85
|
+
catalogSongs(limit?: number, offset?: number, genre?: string, filter?: string | Filter): Promise<SongViewBasic[]>;
|
|
75
86
|
/** An album's tracklist by album at:// URI. */
|
|
76
87
|
albumTracks(uri: string): Promise<SongViewBasic[]>;
|
|
77
88
|
/** An artist's albums by artist at:// URI. */
|
|
@@ -79,8 +90,9 @@ export declare class RockskyClient {
|
|
|
79
90
|
/** An artist's top tracks by artist at:// URI. */
|
|
80
91
|
artistTracks(uri: string, limit?: number, offset?: number): Promise<SongViewBasic[]>;
|
|
81
92
|
/** A social/global scrobbles feed. Pass `did` to scope to an actor and
|
|
82
|
-
* `following = true` for their follow graph.
|
|
83
|
-
|
|
93
|
+
* `following = true` for their follow graph. `filter` takes an RSQL
|
|
94
|
+
* {@link Filter} expression (see {@link ScrobbleFields}). */
|
|
95
|
+
scrobbleFeed(did?: string, following?: boolean, limit?: number, offset?: number, filter?: string | Filter): Promise<ScrobbleViewBasic[]>;
|
|
84
96
|
/** A single scrobble by its at:// URI. */
|
|
85
97
|
scrobble(uri: string): Promise<ScrobbleViewBasic>;
|
|
86
98
|
/** The accounts `actor` follows. */
|
|
@@ -183,5 +195,17 @@ export declare class RockskyClient {
|
|
|
183
195
|
* to mark **all** of the viewer's notifications. Returns the number remaining
|
|
184
196
|
* unread. */
|
|
185
197
|
updateSeen(ids?: string[]): Promise<UpdateSeenOutput>;
|
|
198
|
+
/** Follow an account by DID or handle (`app.rocksky.graph.followAccount`). Auth required. */
|
|
199
|
+
followAccount(account: string): Promise<FollowAccountOutput>;
|
|
200
|
+
/** Unfollow an account by DID or handle (`app.rocksky.graph.unfollowAccount`). Auth required. */
|
|
201
|
+
unfollowAccount(account: string): Promise<UnfollowAccountOutput>;
|
|
202
|
+
/** Submit a scrobble through the AppView (`app.rocksky.scrobble.createScrobble`).
|
|
203
|
+
* Auth required. For direct-to-PDS scrobbling use {@link Agent} instead. */
|
|
204
|
+
createScrobble(input: CreateScrobbleInput): Promise<ScrobbleViewBasic>;
|
|
205
|
+
/** Create or update a mirror source (`app.rocksky.mirror.putMirrorSource`). Auth required. */
|
|
206
|
+
putMirrorSource(input: PutMirrorSourceInput): Promise<MirrorSourceView>;
|
|
207
|
+
/** Patch the viewer's Rockbox audio settings (`app.rocksky.rockbox.putAudioSettings`).
|
|
208
|
+
* Auth required. */
|
|
209
|
+
putAudioSettings(input: PutAudioSettingsInput): Promise<RockboxSettingsView>;
|
|
186
210
|
}
|
|
187
211
|
//# sourceMappingURL=client.d.ts.map
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/dedup.js
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __returnValue = (v) => v;
|
|
3
|
+
function __exportSetter(name, newValue) {
|
|
4
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
5
|
+
}
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, {
|
|
9
|
+
get: all[name],
|
|
10
|
+
enumerable: true,
|
|
11
|
+
configurable: true,
|
|
12
|
+
set: __exportSetter.bind(all, name)
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
16
|
+
|
|
17
|
+
// src/dedup.ts
|
|
18
|
+
import { fromUint8Array } from "@atcute/car";
|
|
19
|
+
import { decode as cborDecode, fromBytes, isBytes, isCidLink } from "@atcute/cbor";
|
|
20
|
+
import { toString as cidToString } from "@atcute/cid";
|
|
21
|
+
import { ClassicLevel } from "classic-level";
|
|
22
|
+
|
|
23
|
+
// src/hash.ts
|
|
24
|
+
import { sha256 } from "@noble/hashes/sha2.js";
|
|
25
|
+
import { bytesToHex } from "@noble/hashes/utils.js";
|
|
26
|
+
function sha256Lower(s) {
|
|
27
|
+
return bytesToHex(sha256(new TextEncoder().encode(s.toLowerCase())));
|
|
28
|
+
}
|
|
29
|
+
function songHash(title, artist, album) {
|
|
30
|
+
return sha256Lower(`${title} - ${artist} - ${album}`);
|
|
31
|
+
}
|
|
32
|
+
function albumHash(album, albumArtist) {
|
|
33
|
+
return sha256Lower(`${album} - ${albumArtist}`);
|
|
34
|
+
}
|
|
35
|
+
function artistHash(albumArtist) {
|
|
36
|
+
return sha256Lower(albumArtist);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// src/dedup.ts
|
|
40
|
+
var C_ARTIST = "app.rocksky.artist";
|
|
41
|
+
var C_ALBUM = "app.rocksky.album";
|
|
42
|
+
var C_SONG = "app.rocksky.song";
|
|
43
|
+
var C_SCROBBLE = "app.rocksky.scrobble";
|
|
44
|
+
var SEP = "\x00";
|
|
45
|
+
function totalIndexed(s) {
|
|
46
|
+
return s.artists + s.albums + s.songs + s.scrobbles;
|
|
47
|
+
}
|
|
48
|
+
function identKey(did, col, hash) {
|
|
49
|
+
return `${did}${SEP}${col}${SEP}${hash}`;
|
|
50
|
+
}
|
|
51
|
+
function scrobbleKey(did, songH, secs) {
|
|
52
|
+
return `${did}${SEP}${C_SCROBBLE}${SEP}${songH}${SEP}${secs}`;
|
|
53
|
+
}
|
|
54
|
+
function primaryFor(did, col, rec) {
|
|
55
|
+
const s = (k) => typeof rec[k] === "string" ? rec[k] : "";
|
|
56
|
+
switch (col) {
|
|
57
|
+
case C_ARTIST:
|
|
58
|
+
return s("name") ? identKey(did, C_ARTIST, artistHash(s("name"))) : undefined;
|
|
59
|
+
case C_ALBUM:
|
|
60
|
+
return s("title") && s("artist") ? identKey(did, C_ALBUM, albumHash(s("title"), s("artist"))) : undefined;
|
|
61
|
+
case C_SONG:
|
|
62
|
+
return s("title") && s("artist") && s("album") ? identKey(did, C_SONG, songHash(s("title"), s("artist"), s("album"))) : undefined;
|
|
63
|
+
case C_SCROBBLE: {
|
|
64
|
+
if (s("title") && s("artist") && s("album") && s("createdAt")) {
|
|
65
|
+
const secs = Math.floor(Date.parse(s("createdAt")) / 1000);
|
|
66
|
+
if (!Number.isNaN(secs))
|
|
67
|
+
return scrobbleKey(did, songHash(s("title"), s("artist"), s("album")), secs);
|
|
68
|
+
}
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
class RockskyIndex {
|
|
76
|
+
db;
|
|
77
|
+
constructor(path) {
|
|
78
|
+
this.db = new ClassicLevel(path, { keyEncoding: "utf8", valueEncoding: "utf8" });
|
|
79
|
+
}
|
|
80
|
+
open() {
|
|
81
|
+
return this.db.open();
|
|
82
|
+
}
|
|
83
|
+
close() {
|
|
84
|
+
return this.db.close();
|
|
85
|
+
}
|
|
86
|
+
async get(key) {
|
|
87
|
+
try {
|
|
88
|
+
return await this.db.get(key);
|
|
89
|
+
} catch {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
songUri(did, title, artist, album) {
|
|
94
|
+
return this.get(identKey(did, C_SONG, songHash(title, artist, album)));
|
|
95
|
+
}
|
|
96
|
+
albumUri(did, album, albumArtist) {
|
|
97
|
+
return this.get(identKey(did, C_ALBUM, albumHash(album, albumArtist)));
|
|
98
|
+
}
|
|
99
|
+
artistUri(did, albumArtist) {
|
|
100
|
+
return this.get(identKey(did, C_ARTIST, artistHash(albumArtist)));
|
|
101
|
+
}
|
|
102
|
+
scrobbleUri(did, title, artist, album, secs) {
|
|
103
|
+
return this.get(scrobbleKey(did, songHash(title, artist, album), secs));
|
|
104
|
+
}
|
|
105
|
+
async putPrimary(did, col, primary, uri) {
|
|
106
|
+
const rkey = uri.slice(uri.lastIndexOf("/") + 1);
|
|
107
|
+
await this.db.batch([
|
|
108
|
+
{ type: "put", key: primary, value: uri },
|
|
109
|
+
{ type: "put", key: `${SEP}rk${SEP}${did}${SEP}${col}${SEP}${rkey}`, value: primary }
|
|
110
|
+
]);
|
|
111
|
+
}
|
|
112
|
+
recordSong(did, title, artist, album, uri) {
|
|
113
|
+
return this.putPrimary(did, C_SONG, identKey(did, C_SONG, songHash(title, artist, album)), uri);
|
|
114
|
+
}
|
|
115
|
+
recordAlbum(did, album, albumArtist, uri) {
|
|
116
|
+
return this.putPrimary(did, C_ALBUM, identKey(did, C_ALBUM, albumHash(album, albumArtist)), uri);
|
|
117
|
+
}
|
|
118
|
+
recordArtist(did, albumArtist, uri) {
|
|
119
|
+
return this.putPrimary(did, C_ARTIST, identKey(did, C_ARTIST, artistHash(albumArtist)), uri);
|
|
120
|
+
}
|
|
121
|
+
recordScrobble(did, title, artist, album, secs, uri) {
|
|
122
|
+
return this.putPrimary(did, C_SCROBBLE, scrobbleKey(did, songHash(title, artist, album), secs), uri);
|
|
123
|
+
}
|
|
124
|
+
cursor(did) {
|
|
125
|
+
return this.get(`${SEP}meta${SEP}cursor${SEP}${did}`).then((v) => v ? Number(v) : 0);
|
|
126
|
+
}
|
|
127
|
+
async setCursor(did, timeUS) {
|
|
128
|
+
await this.db.put(`${SEP}meta${SEP}cursor${SEP}${did}`, String(timeUS));
|
|
129
|
+
}
|
|
130
|
+
async indexCar(did, car) {
|
|
131
|
+
const reader = fromUint8Array(car);
|
|
132
|
+
const blocks = new Map;
|
|
133
|
+
for (const entry of reader)
|
|
134
|
+
blocks.set(cidToString(entry.cid), entry.bytes);
|
|
135
|
+
const root = reader.roots[0];
|
|
136
|
+
if (!root)
|
|
137
|
+
throw new Error("CAR has no root");
|
|
138
|
+
const commit = cborDecode(blocks.get(root.$link));
|
|
139
|
+
const stats = { artists: 0, albums: 0, songs: 0, scrobbles: 0 };
|
|
140
|
+
const ops = [];
|
|
141
|
+
for (const [path, valueLink] of walkMst(blocks, commit.data.$link)) {
|
|
142
|
+
const slash = path.indexOf("/");
|
|
143
|
+
if (slash < 0)
|
|
144
|
+
continue;
|
|
145
|
+
const col = path.slice(0, slash);
|
|
146
|
+
const rkey = path.slice(slash + 1);
|
|
147
|
+
if (col !== C_ARTIST && col !== C_ALBUM && col !== C_SONG && col !== C_SCROBBLE)
|
|
148
|
+
continue;
|
|
149
|
+
const recBytes = blocks.get(valueLink);
|
|
150
|
+
if (!recBytes)
|
|
151
|
+
continue;
|
|
152
|
+
const rec = cborDecode(recBytes);
|
|
153
|
+
const primary = primaryFor(did, col, rec);
|
|
154
|
+
if (!primary)
|
|
155
|
+
continue;
|
|
156
|
+
const uri = `at://${did}/${col}/${rkey}`;
|
|
157
|
+
ops.push({ type: "put", key: primary, value: uri });
|
|
158
|
+
ops.push({ type: "put", key: `${SEP}rk${SEP}${did}${SEP}${col}${SEP}${rkey}`, value: primary });
|
|
159
|
+
if (col === C_ARTIST)
|
|
160
|
+
stats.artists++;
|
|
161
|
+
else if (col === C_ALBUM)
|
|
162
|
+
stats.albums++;
|
|
163
|
+
else if (col === C_SONG)
|
|
164
|
+
stats.songs++;
|
|
165
|
+
else
|
|
166
|
+
stats.scrobbles++;
|
|
167
|
+
}
|
|
168
|
+
if (commit.rev)
|
|
169
|
+
ops.push({ type: "put", key: `${SEP}meta${SEP}rev${SEP}${did}`, value: commit.rev });
|
|
170
|
+
await this.db.batch(ops);
|
|
171
|
+
return stats;
|
|
172
|
+
}
|
|
173
|
+
async applyCommit(did, col, operation, rkey, record) {
|
|
174
|
+
if (operation === "create" || operation === "update") {
|
|
175
|
+
if (!record)
|
|
176
|
+
return;
|
|
177
|
+
const primary = primaryFor(did, col, record);
|
|
178
|
+
if (!primary)
|
|
179
|
+
return;
|
|
180
|
+
await this.putPrimary(did, col, primary, `at://${did}/${col}/${rkey}`);
|
|
181
|
+
} else if (operation === "delete") {
|
|
182
|
+
const rk = `${SEP}rk${SEP}${did}${SEP}${col}${SEP}${rkey}`;
|
|
183
|
+
const primary = await this.get(rk);
|
|
184
|
+
if (primary) {
|
|
185
|
+
await this.db.batch([
|
|
186
|
+
{ type: "del", key: primary },
|
|
187
|
+
{ type: "del", key: rk }
|
|
188
|
+
]);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
function* walkMst(blocks, cid) {
|
|
194
|
+
const raw = blocks.get(cid);
|
|
195
|
+
if (!raw)
|
|
196
|
+
return;
|
|
197
|
+
const node = cborDecode(raw);
|
|
198
|
+
if (node.l && isCidLink(node.l))
|
|
199
|
+
yield* walkMst(blocks, node.l.$link);
|
|
200
|
+
let last = new Uint8Array(0);
|
|
201
|
+
for (const e of node.e) {
|
|
202
|
+
const suffix = isBytes(e.k) ? fromBytes(e.k) : e.k;
|
|
203
|
+
const key = new Uint8Array(e.p + suffix.length);
|
|
204
|
+
key.set(last.subarray(0, e.p), 0);
|
|
205
|
+
key.set(suffix, e.p);
|
|
206
|
+
yield [new TextDecoder().decode(key), e.v.$link];
|
|
207
|
+
last = key;
|
|
208
|
+
if (e.t && isCidLink(e.t))
|
|
209
|
+
yield* walkMst(blocks, e.t.$link);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
export {
|
|
213
|
+
RockskyIndex,
|
|
214
|
+
totalIndexed
|
|
215
|
+
};
|
package/dist/errors.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
/** Error thrown when a Rocksky XRPC call returns a non-2xx `{ error, message }`. */
|
|
2
1
|
export declare class RockskyError extends Error {
|
|
2
|
+
/** The XRPC error code (e.g. "InvalidRequest", "AuthMissing"), when the
|
|
3
|
+
* server sent one. */
|
|
3
4
|
readonly kind?: string;
|
|
4
|
-
|
|
5
|
+
/** The HTTP status of the failed response, when known. */
|
|
6
|
+
readonly status?: number;
|
|
7
|
+
constructor(payload: unknown, status?: number);
|
|
5
8
|
}
|
|
6
9
|
//# sourceMappingURL=errors.d.ts.map
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,YAAa,SAAQ,KAAK;IACrC;0BACsB;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,0DAA0D;IAC1D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;gBAEb,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM;CAO9C"}
|
package/dist/filter.d.ts
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fluent builder for RSQL filter expressions, accepted by the `filter`
|
|
3
|
+
* parameter of the catalog and scrobble-feed queries
|
|
4
|
+
* (app.rocksky.song.getSongs, app.rocksky.artist.getArtists,
|
|
5
|
+
* app.rocksky.album.getAlbums, app.rocksky.scrobble.getScrobbles).
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { Filter } from "@rocksky/sdk";
|
|
9
|
+
*
|
|
10
|
+
* const filter = Filter.eq("artist", "Daft Punk")
|
|
11
|
+
* .and(Filter.gt("duration", 200_000))
|
|
12
|
+
* .or(Filter.in("genre", ["house", "electro"]));
|
|
13
|
+
*
|
|
14
|
+
* await client.catalogSongs(50, 0, undefined, filter);
|
|
15
|
+
* // artist=="Daft Punk";duration=gt=200000,genre=in=(house,electro)
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* String values are quoted and escaped automatically when they contain
|
|
19
|
+
* characters RSQL reserves; `*` wildcards pass through unquoted so
|
|
20
|
+
* `Filter.eq("artist", "Daft*")` performs a case-insensitive match.
|
|
21
|
+
*/
|
|
22
|
+
export type FilterValue = string | number | boolean;
|
|
23
|
+
export declare class Filter {
|
|
24
|
+
private readonly expr;
|
|
25
|
+
private readonly kind;
|
|
26
|
+
private constructor();
|
|
27
|
+
private static comparison;
|
|
28
|
+
private static list;
|
|
29
|
+
/** `field==value` — equals; `*` in string values is a wildcard. */
|
|
30
|
+
static eq(field: string, value: FilterValue): Filter;
|
|
31
|
+
/** `field!=value` — not equals. */
|
|
32
|
+
static ne(field: string, value: FilterValue): Filter;
|
|
33
|
+
/** `field=gt=value` — greater than. */
|
|
34
|
+
static gt(field: string, value: FilterValue): Filter;
|
|
35
|
+
/** `field=ge=value` — greater than or equal. */
|
|
36
|
+
static ge(field: string, value: FilterValue): Filter;
|
|
37
|
+
/** `field=lt=value` — less than. */
|
|
38
|
+
static lt(field: string, value: FilterValue): Filter;
|
|
39
|
+
/** `field=le=value` — less than or equal. */
|
|
40
|
+
static le(field: string, value: FilterValue): Filter;
|
|
41
|
+
/** `field=in=(a,b)` — matches any of the values. */
|
|
42
|
+
static in(field: string, values: FilterValue[]): Filter;
|
|
43
|
+
/** `field=out=(a,b)` — matches none of the values. */
|
|
44
|
+
static out(field: string, values: FilterValue[]): Filter;
|
|
45
|
+
/** `field==null` — the field is NULL. */
|
|
46
|
+
static isNull(field: string): Filter;
|
|
47
|
+
/** `field!=null` — the field is not NULL. */
|
|
48
|
+
static isNotNull(field: string): Filter;
|
|
49
|
+
/** Both sides must match (`;`). An `or` operand is parenthesized to keep RSQL precedence. */
|
|
50
|
+
and(other: Filter): Filter;
|
|
51
|
+
/** Either side may match (`,`). */
|
|
52
|
+
or(other: Filter): Filter;
|
|
53
|
+
private renderIn;
|
|
54
|
+
/** The RSQL expression string to send as the `filter` query param. */
|
|
55
|
+
build(): string;
|
|
56
|
+
toString(): string;
|
|
57
|
+
}
|
|
58
|
+
/** Filterable fields of app.rocksky.song.getSongs. */
|
|
59
|
+
export declare const SongFields: {
|
|
60
|
+
readonly title: "title";
|
|
61
|
+
readonly artist: "artist";
|
|
62
|
+
readonly album: "album";
|
|
63
|
+
readonly albumArtist: "albumArtist";
|
|
64
|
+
readonly genre: "genre";
|
|
65
|
+
readonly composer: "composer";
|
|
66
|
+
readonly label: "label";
|
|
67
|
+
readonly duration: "duration";
|
|
68
|
+
readonly trackNumber: "trackNumber";
|
|
69
|
+
readonly discNumber: "discNumber";
|
|
70
|
+
readonly mbId: "mbId";
|
|
71
|
+
readonly isrc: "isrc";
|
|
72
|
+
readonly sha256: "sha256";
|
|
73
|
+
readonly uri: "uri";
|
|
74
|
+
readonly albumUri: "albumUri";
|
|
75
|
+
readonly artistUri: "artistUri";
|
|
76
|
+
readonly createdAt: "createdAt";
|
|
77
|
+
};
|
|
78
|
+
/** Filterable fields of app.rocksky.album.getAlbums. */
|
|
79
|
+
export declare const AlbumFields: {
|
|
80
|
+
readonly title: "title";
|
|
81
|
+
readonly artist: "artist";
|
|
82
|
+
readonly year: "year";
|
|
83
|
+
readonly releaseDate: "releaseDate";
|
|
84
|
+
readonly sha256: "sha256";
|
|
85
|
+
readonly uri: "uri";
|
|
86
|
+
readonly artistUri: "artistUri";
|
|
87
|
+
readonly createdAt: "createdAt";
|
|
88
|
+
};
|
|
89
|
+
/** Filterable fields of app.rocksky.artist.getArtists. */
|
|
90
|
+
export declare const ArtistFields: {
|
|
91
|
+
readonly name: "name";
|
|
92
|
+
readonly genres: "genres";
|
|
93
|
+
readonly bornIn: "bornIn";
|
|
94
|
+
readonly born: "born";
|
|
95
|
+
readonly died: "died";
|
|
96
|
+
readonly sha256: "sha256";
|
|
97
|
+
readonly uri: "uri";
|
|
98
|
+
readonly createdAt: "createdAt";
|
|
99
|
+
};
|
|
100
|
+
/** Filterable fields of app.rocksky.scrobble.getScrobbles (dotted selectors reach the joined track/user/artist). */
|
|
101
|
+
export declare const ScrobbleFields: {
|
|
102
|
+
readonly uri: "uri";
|
|
103
|
+
readonly date: "date";
|
|
104
|
+
readonly timestamp: "timestamp";
|
|
105
|
+
readonly title: "title";
|
|
106
|
+
readonly artist: "artist";
|
|
107
|
+
readonly album: "album";
|
|
108
|
+
readonly trackTitle: "track.title";
|
|
109
|
+
readonly trackArtist: "track.artist";
|
|
110
|
+
readonly trackAlbum: "track.album";
|
|
111
|
+
readonly trackAlbumArtist: "track.albumArtist";
|
|
112
|
+
readonly trackGenre: "track.genre";
|
|
113
|
+
readonly trackDuration: "track.duration";
|
|
114
|
+
readonly trackIsrc: "track.isrc";
|
|
115
|
+
readonly trackMbId: "track.mbId";
|
|
116
|
+
readonly userDid: "user.did";
|
|
117
|
+
readonly userHandle: "user.handle";
|
|
118
|
+
readonly userDisplayName: "user.displayName";
|
|
119
|
+
readonly artistName: "artist.name";
|
|
120
|
+
readonly artistGenres: "artist.genres";
|
|
121
|
+
};
|
|
122
|
+
//# sourceMappingURL=filter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter.d.ts","sourceRoot":"","sources":["../src/filter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAapD,qBAAa,MAAM;IAEf,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAFvB,OAAO;IAKP,OAAO,CAAC,MAAM,CAAC,UAAU;IAIzB,OAAO,CAAC,MAAM,CAAC,IAAI;IAOnB,mEAAmE;IACnE,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,MAAM;IAIpD,mCAAmC;IACnC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,MAAM;IAIpD,uCAAuC;IACvC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,MAAM;IAIpD,gDAAgD;IAChD,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,MAAM;IAIpD,oCAAoC;IACpC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,MAAM;IAIpD,6CAA6C;IAC7C,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,MAAM;IAIpD,oDAAoD;IACpD,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM;IAIvD,sDAAsD;IACtD,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM;IAIxD,yCAAyC;IACzC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAIpC,6CAA6C;IAC7C,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAIvC,6FAA6F;IAC7F,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAI1B,mCAAmC;IACnC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAIzB,OAAO,CAAC,QAAQ;IAIhB,sEAAsE;IACtE,KAAK,IAAI,MAAM;IAIf,QAAQ,IAAI,MAAM;CAGnB;AAED,sDAAsD;AACtD,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;CAkBb,CAAC;AAEX,wDAAwD;AACxD,eAAO,MAAM,WAAW;;;;;;;;;CASd,CAAC;AAEX,0DAA0D;AAC1D,eAAO,MAAM,YAAY;;;;;;;;;CASf,CAAC;AAEX,oHAAoH;AACpH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;CAoBjB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter.test.d.ts","sourceRoot":"","sources":["../src/filter.test.ts"],"names":[],"mappings":""}
|
|
@@ -224,6 +224,8 @@ export interface ArtistGetArtistsParams {
|
|
|
224
224
|
names?: string;
|
|
225
225
|
/** The genre to filter artists by */
|
|
226
226
|
genre?: string;
|
|
227
|
+
/** RSQL filter expression, e.g. `name==Daft*;genres=in=(house,electro)`. Supports ==, !=, <, <=, >, >=, =in=, =out=, and `;`/`and`, `,`/`or` combinators, `*` wildcards in string values. Filterable fields: name, genres, bornIn, born, died, sha256, uri, createdAt */
|
|
228
|
+
filter?: string;
|
|
227
229
|
}
|
|
228
230
|
export interface ArtistListenerViewBasic {
|
|
229
231
|
/** The unique identifier of the actor. */
|
|
@@ -754,6 +756,8 @@ export interface GetAlbumsParams {
|
|
|
754
756
|
offset?: number;
|
|
755
757
|
/** The genre to filter artists by */
|
|
756
758
|
genre?: string;
|
|
759
|
+
/** RSQL filter expression, e.g. `artist=="Daft Punk";year=ge=2000`. Supports ==, !=, <, <=, >, >=, =in=, =out=, and `;`/`and`, `,`/`or` combinators, `*` wildcards in string values. Filterable fields: title, artist, year, releaseDate, sha256, uri, artistUri, createdAt */
|
|
760
|
+
filter?: string;
|
|
757
761
|
}
|
|
758
762
|
export interface GetAlbumTracksOutput {
|
|
759
763
|
tracks?: SongViewBasic[];
|
|
@@ -1058,6 +1062,8 @@ export interface GetScrobblesParams {
|
|
|
1058
1062
|
limit?: number;
|
|
1059
1063
|
/** The offset for pagination */
|
|
1060
1064
|
offset?: number;
|
|
1065
|
+
/** RSQL filter expression, e.g. `track.artist=="Daft Punk";date=ge=2025-01-01`. Supports ==, !=, <, <=, >, >=, =in=, =out=, and `;`/`and`, `,`/`or` combinators, `*` wildcards in string values. Filterable fields: uri, date, timestamp, title, artist, album, track.title, track.artist, track.album, track.albumArtist, track.genre, track.duration, track.isrc, track.mbId, user.did, user.handle, user.displayName, artist.name, artist.genres */
|
|
1066
|
+
filter?: string;
|
|
1061
1067
|
}
|
|
1062
1068
|
export interface GetShoutRepliesOutput {
|
|
1063
1069
|
shouts?: unknown[];
|
|
@@ -1115,6 +1121,8 @@ export interface GetSongsParams {
|
|
|
1115
1121
|
isrc?: string;
|
|
1116
1122
|
/** Filter songs by Spotify track ID (resolved internally to the Spotify track URL) */
|
|
1117
1123
|
spotifyId?: string;
|
|
1124
|
+
/** RSQL filter expression, e.g. `artist=="Daft Punk";duration=gt=200000`. Supports ==, !=, <, <=, >, >=, =in=, =out=, and `;`/`and`, `,`/`or` combinators, `*` wildcards in string values. Filterable fields: title, artist, album, albumArtist, genre, composer, label, duration, trackNumber, discNumber, mbId, isrc, sha256, uri, albumUri, artistUri, createdAt */
|
|
1125
|
+
filter?: string;
|
|
1118
1126
|
}
|
|
1119
1127
|
export interface GetStarredOutput {
|
|
1120
1128
|
}
|
|
@@ -1362,6 +1370,17 @@ export interface NotificationActor {
|
|
|
1362
1370
|
/** The URL of the actor's avatar image. */
|
|
1363
1371
|
avatar?: Uri;
|
|
1364
1372
|
}
|
|
1373
|
+
/** The song, album, or scrobble a notification relates to, for rich display. */
|
|
1374
|
+
export interface NotificationSubjectView {
|
|
1375
|
+
/** The at-uri of the subject. */
|
|
1376
|
+
uri: string;
|
|
1377
|
+
/** The title of the track or album. */
|
|
1378
|
+
title?: string;
|
|
1379
|
+
/** The artist of the track or album. */
|
|
1380
|
+
artist?: string;
|
|
1381
|
+
/** The album art image URL. */
|
|
1382
|
+
albumArt?: Uri;
|
|
1383
|
+
}
|
|
1365
1384
|
export interface NotificationView {
|
|
1366
1385
|
/** The unique identifier of the notification. */
|
|
1367
1386
|
id: string;
|
|
@@ -1378,6 +1397,7 @@ export interface NotificationView {
|
|
|
1378
1397
|
/** The content of the related shout, if any. */
|
|
1379
1398
|
shoutContent?: string;
|
|
1380
1399
|
actor?: NotificationActor;
|
|
1400
|
+
subject?: NotificationSubjectView;
|
|
1381
1401
|
}
|
|
1382
1402
|
export interface PingOutput {
|
|
1383
1403
|
}
|