district.js 0.6.0 → 0.7.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/client.d.ts +3 -0
- package/dist/client.js +4 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/store.d.ts +41 -0
- package/dist/store.js +90 -0
- package/package.json +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { EventEmitter } from "node:events";
|
|
2
2
|
import http from "node:http";
|
|
3
3
|
import { REST } from "./rest.js";
|
|
4
|
+
import { Store } from "./store.js";
|
|
4
5
|
import { ClientUser, Message, TextChannel, Interaction, User, Space, type ClientContext, type MessageContent } from "./structures.js";
|
|
5
6
|
import { type EventHeaders } from "./webhook.js";
|
|
6
7
|
import type { ClientOptions, ReactionEvent, DeletedMessage } from "./types.js";
|
|
@@ -17,6 +18,8 @@ export interface ClientEvents {
|
|
|
17
18
|
}
|
|
18
19
|
export declare class Client extends EventEmitter implements ClientContext {
|
|
19
20
|
readonly rest: REST;
|
|
21
|
+
/** The bot's persistent document store (`client.store.collection("users")…`). */
|
|
22
|
+
readonly store: Store;
|
|
20
23
|
/** The bot's own identity — populated after `login()`. */
|
|
21
24
|
user: ClientUser | null;
|
|
22
25
|
private signingSecret?;
|
package/dist/client.js
CHANGED
|
@@ -3,10 +3,13 @@
|
|
|
3
3
|
import { EventEmitter } from "node:events";
|
|
4
4
|
import http from "node:http";
|
|
5
5
|
import { REST } from "./rest.js";
|
|
6
|
+
import { Store } from "./store.js";
|
|
6
7
|
import { ClientUser, Message, TextChannel, Interaction, User, Space } from "./structures.js";
|
|
7
8
|
import { verifyAndParse } from "./webhook.js";
|
|
8
9
|
export class Client extends EventEmitter {
|
|
9
10
|
rest;
|
|
11
|
+
/** The bot's persistent document store (`client.store.collection("users")…`). */
|
|
12
|
+
store;
|
|
10
13
|
/** The bot's own identity — populated after `login()`. */
|
|
11
14
|
user = null;
|
|
12
15
|
signingSecret;
|
|
@@ -15,6 +18,7 @@ export class Client extends EventEmitter {
|
|
|
15
18
|
if (!options?.token)
|
|
16
19
|
throw new Error("district.js: `token` is required");
|
|
17
20
|
this.rest = new REST(options.token, options.apiBase);
|
|
21
|
+
this.store = new Store(this.rest);
|
|
18
22
|
this.signingSecret = options.signingSecret;
|
|
19
23
|
}
|
|
20
24
|
// ── typed emitter surface ──────────────────────────────────────────────────
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export { REST, DistrictAPIError, DEFAULT_API_BASE } from "./rest.js";
|
|
|
3
3
|
export { ClientUser, Message, TextChannel, Interaction, User, Space, SpaceMember, Role, Attachment, Ban, Invite, Webhook, type ClientContext, type MessageContent, } from "./structures.js";
|
|
4
4
|
export { EmbedBuilder, resolveEmbed, type EmbedData, type EmbedField, type EmbedResolvable } from "./embed.js";
|
|
5
5
|
export { ButtonBuilder, ActionRowBuilder, StringSelectMenuBuilder, ModalBuilder, TextInputBuilder, resolveComponent, type ButtonStyle, type ButtonData, type SelectData, type SelectOption, type ComponentData, type ModalData, type TextInputData, type ActionRowData, type ComponentResolvable, } from "./components.js";
|
|
6
|
+
export { Store, StoreCollection, type StoreDocument } from "./store.js";
|
|
6
7
|
export { Collection } from "./collection.js";
|
|
7
8
|
export { PermissionsBitField, type PermissionResolvable, type PermissionFlagName } from "./permissions.js";
|
|
8
9
|
export { Formatters } from "./formatters.js";
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ export { REST, DistrictAPIError, DEFAULT_API_BASE } from "./rest.js";
|
|
|
9
9
|
export { ClientUser, Message, TextChannel, Interaction, User, Space, SpaceMember, Role, Attachment, Ban, Invite, Webhook, } from "./structures.js";
|
|
10
10
|
export { EmbedBuilder, resolveEmbed } from "./embed.js";
|
|
11
11
|
export { ButtonBuilder, ActionRowBuilder, StringSelectMenuBuilder, ModalBuilder, TextInputBuilder, resolveComponent, } from "./components.js";
|
|
12
|
+
export { Store, StoreCollection } from "./store.js";
|
|
12
13
|
export { Collection } from "./collection.js";
|
|
13
14
|
export { PermissionsBitField } from "./permissions.js";
|
|
14
15
|
export { Formatters } from "./formatters.js";
|
package/dist/store.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { REST } from "./rest.js";
|
|
2
|
+
/** One stored document: its `key` and JSON `value` (+ last-updated time on reads). */
|
|
3
|
+
export interface StoreDocument<T = any> {
|
|
4
|
+
key: string;
|
|
5
|
+
value: T;
|
|
6
|
+
updatedAt?: string;
|
|
7
|
+
}
|
|
8
|
+
/** A handle to one collection of documents (e.g. "users", "guilds"). */
|
|
9
|
+
export declare class StoreCollection {
|
|
10
|
+
private readonly rest;
|
|
11
|
+
readonly name: string;
|
|
12
|
+
constructor(rest: REST, name: string);
|
|
13
|
+
/** Read one document's value, or `null` if it doesn't exist. */
|
|
14
|
+
get<T = any>(key: string): Promise<T | null>;
|
|
15
|
+
/** Create or fully replace a document. Returns the stored value. */
|
|
16
|
+
set<T = any>(key: string, value: T): Promise<T>;
|
|
17
|
+
/** Shallow-merge `partial` into an existing document (creates it if absent).
|
|
18
|
+
* Returns the merged value. */
|
|
19
|
+
update<T = any>(key: string, partial: Partial<T>): Promise<T>;
|
|
20
|
+
/** Delete a document. No-op if it doesn't exist. */
|
|
21
|
+
delete(key: string): Promise<void>;
|
|
22
|
+
/** Atomically add `amount` to a numeric field (default field: `value`). Safe
|
|
23
|
+
* under concurrency — two simultaneous increments never lose a write. Pass an
|
|
24
|
+
* array for a nested path, e.g. `increment(id, ["stats", "wins"], 1)`. Returns
|
|
25
|
+
* the whole updated document value. */
|
|
26
|
+
increment<T = any>(key: string, field?: string | string[], amount?: number): Promise<T>;
|
|
27
|
+
/** List documents in this collection (newest-updated first by default). */
|
|
28
|
+
all<T = any>(opts?: {
|
|
29
|
+
limit?: number;
|
|
30
|
+
offset?: number;
|
|
31
|
+
order?: "asc" | "desc";
|
|
32
|
+
}): Promise<StoreDocument<T>[]>;
|
|
33
|
+
}
|
|
34
|
+
/** The bot's persistent store. Get a collection with `.collection(name)`. */
|
|
35
|
+
export declare class Store {
|
|
36
|
+
private readonly rest;
|
|
37
|
+
private readonly cache;
|
|
38
|
+
constructor(rest: REST);
|
|
39
|
+
/** A handle to a named collection (memoized). */
|
|
40
|
+
collection(name: string): StoreCollection;
|
|
41
|
+
}
|
package/dist/store.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// Persistent bot storage — the district.js equivalent of "bring your own
|
|
2
|
+
// database". Each bot gets an isolated document store, addressed as
|
|
3
|
+
// `collection` → `key` → JSON `value`, so you can model users, balances,
|
|
4
|
+
// transactions, leaderboards, config… anything a data-heavy bot needs.
|
|
5
|
+
//
|
|
6
|
+
// const users = client.store.collection("users");
|
|
7
|
+
// await users.set("42", { balance: 100, streak: 0 });
|
|
8
|
+
// const me = await users.get<{ balance: number }>("42");
|
|
9
|
+
// await users.increment("42", "balance", 250); // atomic — safe under load
|
|
10
|
+
// const leaderboard = await users.all({ limit: 10 });
|
|
11
|
+
//
|
|
12
|
+
// Everything is scoped to your application automatically; a bot can only ever
|
|
13
|
+
// see its own data.
|
|
14
|
+
import { DistrictAPIError } from "./rest.js";
|
|
15
|
+
const enc = encodeURIComponent;
|
|
16
|
+
/** A handle to one collection of documents (e.g. "users", "guilds"). */
|
|
17
|
+
export class StoreCollection {
|
|
18
|
+
rest;
|
|
19
|
+
name;
|
|
20
|
+
constructor(rest, name) {
|
|
21
|
+
this.rest = rest;
|
|
22
|
+
this.name = name;
|
|
23
|
+
}
|
|
24
|
+
/** Read one document's value, or `null` if it doesn't exist. */
|
|
25
|
+
async get(key) {
|
|
26
|
+
try {
|
|
27
|
+
const r = await this.rest.get(`/storage/${enc(this.name)}/${enc(key)}`);
|
|
28
|
+
return r.value;
|
|
29
|
+
}
|
|
30
|
+
catch (e) {
|
|
31
|
+
if (e instanceof DistrictAPIError && e.status === 404)
|
|
32
|
+
return null;
|
|
33
|
+
throw e;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/** Create or fully replace a document. Returns the stored value. */
|
|
37
|
+
async set(key, value) {
|
|
38
|
+
const r = await this.rest.put(`/storage/${enc(this.name)}/${enc(key)}`, value);
|
|
39
|
+
return r.value;
|
|
40
|
+
}
|
|
41
|
+
/** Shallow-merge `partial` into an existing document (creates it if absent).
|
|
42
|
+
* Returns the merged value. */
|
|
43
|
+
async update(key, partial) {
|
|
44
|
+
const r = await this.rest.patch(`/storage/${enc(this.name)}/${enc(key)}`, partial);
|
|
45
|
+
return r.value;
|
|
46
|
+
}
|
|
47
|
+
/** Delete a document. No-op if it doesn't exist. */
|
|
48
|
+
async delete(key) {
|
|
49
|
+
await this.rest.delete(`/storage/${enc(this.name)}/${enc(key)}`);
|
|
50
|
+
}
|
|
51
|
+
/** Atomically add `amount` to a numeric field (default field: `value`). Safe
|
|
52
|
+
* under concurrency — two simultaneous increments never lose a write. Pass an
|
|
53
|
+
* array for a nested path, e.g. `increment(id, ["stats", "wins"], 1)`. Returns
|
|
54
|
+
* the whole updated document value. */
|
|
55
|
+
async increment(key, field = "value", amount = 1) {
|
|
56
|
+
const path = Array.isArray(field) ? field : [field];
|
|
57
|
+
const r = await this.rest.post(`/storage/${enc(this.name)}/${enc(key)}/increment`, { path, amount });
|
|
58
|
+
return r.value;
|
|
59
|
+
}
|
|
60
|
+
/** List documents in this collection (newest-updated first by default). */
|
|
61
|
+
async all(opts = {}) {
|
|
62
|
+
const q = new URLSearchParams();
|
|
63
|
+
if (opts.limit != null)
|
|
64
|
+
q.set("limit", String(opts.limit));
|
|
65
|
+
if (opts.offset != null)
|
|
66
|
+
q.set("offset", String(opts.offset));
|
|
67
|
+
if (opts.order)
|
|
68
|
+
q.set("order", opts.order);
|
|
69
|
+
const qs = q.toString();
|
|
70
|
+
const r = await this.rest.get(`/storage/${enc(this.name)}${qs ? `?${qs}` : ""}`);
|
|
71
|
+
return r.items ?? [];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/** The bot's persistent store. Get a collection with `.collection(name)`. */
|
|
75
|
+
export class Store {
|
|
76
|
+
rest;
|
|
77
|
+
cache = new Map();
|
|
78
|
+
constructor(rest) {
|
|
79
|
+
this.rest = rest;
|
|
80
|
+
}
|
|
81
|
+
/** A handle to a named collection (memoized). */
|
|
82
|
+
collection(name) {
|
|
83
|
+
let c = this.cache.get(name);
|
|
84
|
+
if (!c) {
|
|
85
|
+
c = new StoreCollection(this.rest, name);
|
|
86
|
+
this.cache.set(name, c);
|
|
87
|
+
}
|
|
88
|
+
return c;
|
|
89
|
+
}
|
|
90
|
+
}
|
package/package.json
CHANGED