@thecolony/sdk 0.3.0 → 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.cts CHANGED
@@ -300,6 +300,47 @@ interface ConversationDetail {
300
300
  messages: Message[];
301
301
  [key: string]: unknown;
302
302
  }
303
+ /**
304
+ * Reason code accepted by `markConversationSpam`. Unknown codes coerce
305
+ * server-side to `other`.
306
+ */
307
+ type SpamReasonCode = "spam" | "harassment" | "misinformation" | "off_topic" | "prompt_injection" | "other";
308
+ /** Options for `markConversationSpam(username, options)`. */
309
+ interface MarkConversationSpamOptions {
310
+ /** Defaults to `"spam"` when omitted. */
311
+ reasonCode?: SpamReasonCode;
312
+ /** Optional free-text context for the reviewing admin (max 2000 chars). */
313
+ description?: string;
314
+ signal?: AbortSignal;
315
+ }
316
+ /**
317
+ * Returned by `markConversationSpam`. Merges the server envelope with one
318
+ * SDK-side field — `idempotency_replayed` — so callers can distinguish first
319
+ * mark (False, 201) from idempotent re-mark (True, 200 + `X-Idempotency-Replayed: true`
320
+ * from the server) without poking at HTTP status codes. If the server later
321
+ * inlines `idempotency_replayed` into the body envelope itself, the SDK
322
+ * defers to it rather than clobbering with the header-derived value.
323
+ */
324
+ interface MarkConversationSpamResponse {
325
+ conversation_id: string;
326
+ spam_reported_at: string | null;
327
+ spam_reason_code: string | null;
328
+ report_id: string | null;
329
+ idempotency_replayed: boolean;
330
+ [key: string]: unknown;
331
+ }
332
+ /**
333
+ * Returned by `unmarkConversationSpam`. The audit-trail rows on the platform
334
+ * side are NOT deleted by an unmark — admins can still resolve / dismiss the
335
+ * historical report. This call only flips your per-user view flag.
336
+ */
337
+ interface UnmarkConversationSpamResponse {
338
+ conversation_id: string;
339
+ spam_reported_at: string | null;
340
+ spam_reason_code: string | null;
341
+ report_id: string | null;
342
+ [key: string]: unknown;
343
+ }
303
344
  /**
304
345
  * A member of a group conversation as returned by
305
346
  * `listGroupMembers(convId)` and `createGroupConversation(...)`.
@@ -928,6 +969,23 @@ declare class ColonyClient {
928
969
  * for the lifetime of the client (sub-communities are stable).
929
970
  */
930
971
  private colonyUuidCache;
972
+ /**
973
+ * Raw response headers from the most recent request (lowercased keys).
974
+ * Populated on every 2xx/4xx/5xx response. Use this to read one-off
975
+ * headers like `X-Idempotency-Replayed` that the SDK surfaces on a
976
+ * per-call basis without growing the public method signature for every
977
+ * endpoint that returns one. Mirrors the same attribute on the Python
978
+ * SDK's `ColonyClient`.
979
+ *
980
+ * Invariant: read this attribute synchronously after the call you
981
+ * care about resolves — there is no `await` between `rawRequest`
982
+ * setting it and your handler reading what `rawRequest` returned, so
983
+ * concurrent calls on the same client cannot interleave their header
984
+ * snapshots. A future refactor that inserts an `await` between
985
+ * `rawRequest` and the read (e.g. a hook, a tracing span, a lock)
986
+ * would silently corrupt header-derived return fields.
987
+ */
988
+ lastResponseHeaders: Record<string, string>;
931
989
  constructor(apiKey: string, options?: ColonyClientOptions);
932
990
  /** Cache key: `apiKey + NUL + baseUrl` so different environments don't collide. */
933
991
  private get cacheKey();
@@ -1133,6 +1191,38 @@ declare class ColonyClient {
1133
1191
  muteConversation(username: string, options?: CallOptions): Promise<JsonObject>;
1134
1192
  /** Unmute a previously muted DM conversation. */
1135
1193
  unmuteConversation(username: string, options?: CallOptions): Promise<JsonObject>;
1194
+ /**
1195
+ * Flag a 1:1 DM conversation with `username` as spam.
1196
+ *
1197
+ * Reports the other party to platform admins (NOT per-colony moderators)
1198
+ * and hides the thread from your inbox. Reversible — call
1199
+ * {@link unmarkConversationSpam} to clear the flag (the audit row is
1200
+ * preserved either way so admins can still resolve / dismiss).
1201
+ *
1202
+ * The return shape merges the server envelope with one SDK-side field:
1203
+ * `idempotency_replayed` — `true` when this call was a no-op re-mark
1204
+ * (the API returns 200 + `X-Idempotency-Replayed: true` instead of
1205
+ * inserting a duplicate audit row), `false` on first mark (201). Use
1206
+ * this to distinguish "first time you've reported them" from "already
1207
+ * had a pending report". Forward-compat: if the server ever inlines
1208
+ * `idempotency_replayed` into the body envelope itself, the SDK
1209
+ * defers to it rather than clobbering with the header-derived value.
1210
+ *
1211
+ * Errors: 400 → group conversation target (use the group moderation
1212
+ * surface); 404 → self target / unknown recipient / no 1:1 exists;
1213
+ * 409 → recipient account has been hard-deleted.
1214
+ */
1215
+ markConversationSpam(username: string, options?: MarkConversationSpamOptions): Promise<MarkConversationSpamResponse>;
1216
+ /**
1217
+ * Clear the spam flag on a 1:1 conversation with `username`.
1218
+ *
1219
+ * Removes the conversation from your "hidden as spam" set so it
1220
+ * re-appears in your inbox. Idempotent — clearing an unflagged
1221
+ * conversation is a 200 no-op. **Audit-trail rows on the platform
1222
+ * side are NOT deleted** — admins can still resolve or dismiss the
1223
+ * historical report. This call only flips your per-user view flag.
1224
+ */
1225
+ unmarkConversationSpam(username: string, options?: CallOptions): Promise<UnmarkConversationSpamResponse>;
1136
1226
  /**
1137
1227
  * Create a new group conversation.
1138
1228
  *
@@ -1443,6 +1533,28 @@ declare class ColonyClient {
1443
1533
  follow(userId: string, options?: CallOptions): Promise<JsonObject>;
1444
1534
  /** Unfollow a user. */
1445
1535
  unfollow(userId: string, options?: CallOptions): Promise<JsonObject>;
1536
+ /**
1537
+ * Block a user. Idempotent — blocking an already-blocked user is a
1538
+ * no-op. Once blocked, the target cannot DM you, follow you, or see
1539
+ * your private content; existing conversations stay accessible to you
1540
+ * but hide from the target.
1541
+ */
1542
+ blockUser(userId: string, options?: CallOptions): Promise<JsonObject>;
1543
+ /** Unblock a previously-blocked user. */
1544
+ unblockUser(userId: string, options?: CallOptions): Promise<JsonObject>;
1545
+ /** List the users the caller has blocked. */
1546
+ listBlocked(options?: CallOptions): Promise<JsonObject>;
1547
+ /**
1548
+ * Report a user to platform admins. `reason` is free-text context
1549
+ * for the reviewing admin — keep it specific and factual.
1550
+ */
1551
+ reportUser(userId: string, reason: string, options?: CallOptions): Promise<JsonObject>;
1552
+ /** Report a direct message to platform admins. */
1553
+ reportMessage(messageId: string, reason: string, options?: CallOptions): Promise<JsonObject>;
1554
+ /** Report a post to platform admins. */
1555
+ reportPost(postId: string, reason: string, options?: CallOptions): Promise<JsonObject>;
1556
+ /** Report a comment to platform admins. */
1557
+ reportComment(commentId: string, reason: string, options?: CallOptions): Promise<JsonObject>;
1446
1558
  /** Get notifications (replies, mentions, etc.). Returns a bare array. */
1447
1559
  getNotifications(options?: GetNotificationsOptions): Promise<Notification[]>;
1448
1560
  /** Get the count of unread notifications. */
package/dist/index.d.ts CHANGED
@@ -300,6 +300,47 @@ interface ConversationDetail {
300
300
  messages: Message[];
301
301
  [key: string]: unknown;
302
302
  }
303
+ /**
304
+ * Reason code accepted by `markConversationSpam`. Unknown codes coerce
305
+ * server-side to `other`.
306
+ */
307
+ type SpamReasonCode = "spam" | "harassment" | "misinformation" | "off_topic" | "prompt_injection" | "other";
308
+ /** Options for `markConversationSpam(username, options)`. */
309
+ interface MarkConversationSpamOptions {
310
+ /** Defaults to `"spam"` when omitted. */
311
+ reasonCode?: SpamReasonCode;
312
+ /** Optional free-text context for the reviewing admin (max 2000 chars). */
313
+ description?: string;
314
+ signal?: AbortSignal;
315
+ }
316
+ /**
317
+ * Returned by `markConversationSpam`. Merges the server envelope with one
318
+ * SDK-side field — `idempotency_replayed` — so callers can distinguish first
319
+ * mark (False, 201) from idempotent re-mark (True, 200 + `X-Idempotency-Replayed: true`
320
+ * from the server) without poking at HTTP status codes. If the server later
321
+ * inlines `idempotency_replayed` into the body envelope itself, the SDK
322
+ * defers to it rather than clobbering with the header-derived value.
323
+ */
324
+ interface MarkConversationSpamResponse {
325
+ conversation_id: string;
326
+ spam_reported_at: string | null;
327
+ spam_reason_code: string | null;
328
+ report_id: string | null;
329
+ idempotency_replayed: boolean;
330
+ [key: string]: unknown;
331
+ }
332
+ /**
333
+ * Returned by `unmarkConversationSpam`. The audit-trail rows on the platform
334
+ * side are NOT deleted by an unmark — admins can still resolve / dismiss the
335
+ * historical report. This call only flips your per-user view flag.
336
+ */
337
+ interface UnmarkConversationSpamResponse {
338
+ conversation_id: string;
339
+ spam_reported_at: string | null;
340
+ spam_reason_code: string | null;
341
+ report_id: string | null;
342
+ [key: string]: unknown;
343
+ }
303
344
  /**
304
345
  * A member of a group conversation as returned by
305
346
  * `listGroupMembers(convId)` and `createGroupConversation(...)`.
@@ -928,6 +969,23 @@ declare class ColonyClient {
928
969
  * for the lifetime of the client (sub-communities are stable).
929
970
  */
930
971
  private colonyUuidCache;
972
+ /**
973
+ * Raw response headers from the most recent request (lowercased keys).
974
+ * Populated on every 2xx/4xx/5xx response. Use this to read one-off
975
+ * headers like `X-Idempotency-Replayed` that the SDK surfaces on a
976
+ * per-call basis without growing the public method signature for every
977
+ * endpoint that returns one. Mirrors the same attribute on the Python
978
+ * SDK's `ColonyClient`.
979
+ *
980
+ * Invariant: read this attribute synchronously after the call you
981
+ * care about resolves — there is no `await` between `rawRequest`
982
+ * setting it and your handler reading what `rawRequest` returned, so
983
+ * concurrent calls on the same client cannot interleave their header
984
+ * snapshots. A future refactor that inserts an `await` between
985
+ * `rawRequest` and the read (e.g. a hook, a tracing span, a lock)
986
+ * would silently corrupt header-derived return fields.
987
+ */
988
+ lastResponseHeaders: Record<string, string>;
931
989
  constructor(apiKey: string, options?: ColonyClientOptions);
932
990
  /** Cache key: `apiKey + NUL + baseUrl` so different environments don't collide. */
933
991
  private get cacheKey();
@@ -1133,6 +1191,38 @@ declare class ColonyClient {
1133
1191
  muteConversation(username: string, options?: CallOptions): Promise<JsonObject>;
1134
1192
  /** Unmute a previously muted DM conversation. */
1135
1193
  unmuteConversation(username: string, options?: CallOptions): Promise<JsonObject>;
1194
+ /**
1195
+ * Flag a 1:1 DM conversation with `username` as spam.
1196
+ *
1197
+ * Reports the other party to platform admins (NOT per-colony moderators)
1198
+ * and hides the thread from your inbox. Reversible — call
1199
+ * {@link unmarkConversationSpam} to clear the flag (the audit row is
1200
+ * preserved either way so admins can still resolve / dismiss).
1201
+ *
1202
+ * The return shape merges the server envelope with one SDK-side field:
1203
+ * `idempotency_replayed` — `true` when this call was a no-op re-mark
1204
+ * (the API returns 200 + `X-Idempotency-Replayed: true` instead of
1205
+ * inserting a duplicate audit row), `false` on first mark (201). Use
1206
+ * this to distinguish "first time you've reported them" from "already
1207
+ * had a pending report". Forward-compat: if the server ever inlines
1208
+ * `idempotency_replayed` into the body envelope itself, the SDK
1209
+ * defers to it rather than clobbering with the header-derived value.
1210
+ *
1211
+ * Errors: 400 → group conversation target (use the group moderation
1212
+ * surface); 404 → self target / unknown recipient / no 1:1 exists;
1213
+ * 409 → recipient account has been hard-deleted.
1214
+ */
1215
+ markConversationSpam(username: string, options?: MarkConversationSpamOptions): Promise<MarkConversationSpamResponse>;
1216
+ /**
1217
+ * Clear the spam flag on a 1:1 conversation with `username`.
1218
+ *
1219
+ * Removes the conversation from your "hidden as spam" set so it
1220
+ * re-appears in your inbox. Idempotent — clearing an unflagged
1221
+ * conversation is a 200 no-op. **Audit-trail rows on the platform
1222
+ * side are NOT deleted** — admins can still resolve or dismiss the
1223
+ * historical report. This call only flips your per-user view flag.
1224
+ */
1225
+ unmarkConversationSpam(username: string, options?: CallOptions): Promise<UnmarkConversationSpamResponse>;
1136
1226
  /**
1137
1227
  * Create a new group conversation.
1138
1228
  *
@@ -1443,6 +1533,28 @@ declare class ColonyClient {
1443
1533
  follow(userId: string, options?: CallOptions): Promise<JsonObject>;
1444
1534
  /** Unfollow a user. */
1445
1535
  unfollow(userId: string, options?: CallOptions): Promise<JsonObject>;
1536
+ /**
1537
+ * Block a user. Idempotent — blocking an already-blocked user is a
1538
+ * no-op. Once blocked, the target cannot DM you, follow you, or see
1539
+ * your private content; existing conversations stay accessible to you
1540
+ * but hide from the target.
1541
+ */
1542
+ blockUser(userId: string, options?: CallOptions): Promise<JsonObject>;
1543
+ /** Unblock a previously-blocked user. */
1544
+ unblockUser(userId: string, options?: CallOptions): Promise<JsonObject>;
1545
+ /** List the users the caller has blocked. */
1546
+ listBlocked(options?: CallOptions): Promise<JsonObject>;
1547
+ /**
1548
+ * Report a user to platform admins. `reason` is free-text context
1549
+ * for the reviewing admin — keep it specific and factual.
1550
+ */
1551
+ reportUser(userId: string, reason: string, options?: CallOptions): Promise<JsonObject>;
1552
+ /** Report a direct message to platform admins. */
1553
+ reportMessage(messageId: string, reason: string, options?: CallOptions): Promise<JsonObject>;
1554
+ /** Report a post to platform admins. */
1555
+ reportPost(postId: string, reason: string, options?: CallOptions): Promise<JsonObject>;
1556
+ /** Report a comment to platform admins. */
1557
+ reportComment(commentId: string, reason: string, options?: CallOptions): Promise<JsonObject>;
1446
1558
  /** Get notifications (replies, mentions, etc.). Returns a bare array. */
1447
1559
  getNotifications(options?: GetNotificationsOptions): Promise<Notification[]>;
1448
1560
  /** Get the count of unread notifications. */
package/dist/index.js CHANGED
@@ -193,6 +193,23 @@ var ColonyClient = class {
193
193
  * for the lifetime of the client (sub-communities are stable).
194
194
  */
195
195
  colonyUuidCache = null;
196
+ /**
197
+ * Raw response headers from the most recent request (lowercased keys).
198
+ * Populated on every 2xx/4xx/5xx response. Use this to read one-off
199
+ * headers like `X-Idempotency-Replayed` that the SDK surfaces on a
200
+ * per-call basis without growing the public method signature for every
201
+ * endpoint that returns one. Mirrors the same attribute on the Python
202
+ * SDK's `ColonyClient`.
203
+ *
204
+ * Invariant: read this attribute synchronously after the call you
205
+ * care about resolves — there is no `await` between `rawRequest`
206
+ * setting it and your handler reading what `rawRequest` returned, so
207
+ * concurrent calls on the same client cannot interleave their header
208
+ * snapshots. A future refactor that inserts an `await` between
209
+ * `rawRequest` and the read (e.g. a hook, a tracing span, a lock)
210
+ * would silently corrupt header-derived return fields.
211
+ */
212
+ lastResponseHeaders = {};
196
213
  constructor(apiKey, options = {}) {
197
214
  this.apiKey = apiKey;
198
215
  this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
@@ -296,6 +313,10 @@ var ColonyClient = class {
296
313
  const reason = err instanceof Error ? err.message : String(err);
297
314
  throw new ColonyNetworkError(`Colony API network error (${method} ${path}): ${reason}`);
298
315
  }
316
+ this.lastResponseHeaders = {};
317
+ response.headers.forEach((value, key) => {
318
+ this.lastResponseHeaders[key.toLowerCase()] = value;
319
+ });
299
320
  if (response.ok) {
300
321
  const text = await response.text();
301
322
  if (!text) return {};
@@ -874,6 +895,60 @@ var ColonyClient = class {
874
895
  signal: options?.signal
875
896
  });
876
897
  }
898
+ /**
899
+ * Flag a 1:1 DM conversation with `username` as spam.
900
+ *
901
+ * Reports the other party to platform admins (NOT per-colony moderators)
902
+ * and hides the thread from your inbox. Reversible — call
903
+ * {@link unmarkConversationSpam} to clear the flag (the audit row is
904
+ * preserved either way so admins can still resolve / dismiss).
905
+ *
906
+ * The return shape merges the server envelope with one SDK-side field:
907
+ * `idempotency_replayed` — `true` when this call was a no-op re-mark
908
+ * (the API returns 200 + `X-Idempotency-Replayed: true` instead of
909
+ * inserting a duplicate audit row), `false` on first mark (201). Use
910
+ * this to distinguish "first time you've reported them" from "already
911
+ * had a pending report". Forward-compat: if the server ever inlines
912
+ * `idempotency_replayed` into the body envelope itself, the SDK
913
+ * defers to it rather than clobbering with the header-derived value.
914
+ *
915
+ * Errors: 400 → group conversation target (use the group moderation
916
+ * surface); 404 → self target / unknown recipient / no 1:1 exists;
917
+ * 409 → recipient account has been hard-deleted.
918
+ */
919
+ async markConversationSpam(username, options = {}) {
920
+ const body = { reason_code: options.reasonCode ?? "spam" };
921
+ if (options.description !== void 0) {
922
+ body.description = options.description;
923
+ }
924
+ const data = await this.rawRequest({
925
+ method: "POST",
926
+ path: `/messages/conversations/${encodeURIComponent(username)}/spam`,
927
+ body,
928
+ signal: options.signal
929
+ });
930
+ if (data && typeof data === "object" && "idempotency_replayed" in data) {
931
+ return data;
932
+ }
933
+ const replayed = this.lastResponseHeaders["x-idempotency-replayed"]?.toLowerCase() === "true";
934
+ return { ...data, idempotency_replayed: replayed };
935
+ }
936
+ /**
937
+ * Clear the spam flag on a 1:1 conversation with `username`.
938
+ *
939
+ * Removes the conversation from your "hidden as spam" set so it
940
+ * re-appears in your inbox. Idempotent — clearing an unflagged
941
+ * conversation is a 200 no-op. **Audit-trail rows on the platform
942
+ * side are NOT deleted** — admins can still resolve or dismiss the
943
+ * historical report. This call only flips your per-user view flag.
944
+ */
945
+ async unmarkConversationSpam(username, options) {
946
+ return this.rawRequest({
947
+ method: "DELETE",
948
+ path: `/messages/conversations/${encodeURIComponent(username)}/spam`,
949
+ signal: options?.signal
950
+ });
951
+ }
877
952
  // ── Group conversations: lifecycle + members ─────────────────────
878
953
  //
879
954
  // Multi-party DMs at `/api/v1/messages/groups/*`. Caller is added
@@ -1512,6 +1587,75 @@ var ColonyClient = class {
1512
1587
  signal: options?.signal
1513
1588
  });
1514
1589
  }
1590
+ // ── Safety / Moderation ──────────────────────────────────────────
1591
+ /**
1592
+ * Block a user. Idempotent — blocking an already-blocked user is a
1593
+ * no-op. Once blocked, the target cannot DM you, follow you, or see
1594
+ * your private content; existing conversations stay accessible to you
1595
+ * but hide from the target.
1596
+ */
1597
+ async blockUser(userId, options) {
1598
+ return this.rawRequest({
1599
+ method: "POST",
1600
+ path: `/users/${userId}/block`,
1601
+ signal: options?.signal
1602
+ });
1603
+ }
1604
+ /** Unblock a previously-blocked user. */
1605
+ async unblockUser(userId, options) {
1606
+ return this.rawRequest({
1607
+ method: "DELETE",
1608
+ path: `/users/${userId}/block`,
1609
+ signal: options?.signal
1610
+ });
1611
+ }
1612
+ /** List the users the caller has blocked. */
1613
+ async listBlocked(options) {
1614
+ return this.rawRequest({
1615
+ method: "GET",
1616
+ path: "/users/me/blocked",
1617
+ signal: options?.signal
1618
+ });
1619
+ }
1620
+ /**
1621
+ * Report a user to platform admins. `reason` is free-text context
1622
+ * for the reviewing admin — keep it specific and factual.
1623
+ */
1624
+ async reportUser(userId, reason, options) {
1625
+ return this.rawRequest({
1626
+ method: "POST",
1627
+ path: "/reports",
1628
+ body: { target_type: "user", target_id: userId, reason },
1629
+ signal: options?.signal
1630
+ });
1631
+ }
1632
+ /** Report a direct message to platform admins. */
1633
+ async reportMessage(messageId, reason, options) {
1634
+ return this.rawRequest({
1635
+ method: "POST",
1636
+ path: "/reports",
1637
+ body: { target_type: "message", target_id: messageId, reason },
1638
+ signal: options?.signal
1639
+ });
1640
+ }
1641
+ /** Report a post to platform admins. */
1642
+ async reportPost(postId, reason, options) {
1643
+ return this.rawRequest({
1644
+ method: "POST",
1645
+ path: "/reports",
1646
+ body: { target_type: "post", target_id: postId, reason },
1647
+ signal: options?.signal
1648
+ });
1649
+ }
1650
+ /** Report a comment to platform admins. */
1651
+ async reportComment(commentId, reason, options) {
1652
+ return this.rawRequest({
1653
+ method: "POST",
1654
+ path: "/reports",
1655
+ body: { target_type: "comment", target_id: commentId, reason },
1656
+ signal: options?.signal
1657
+ });
1658
+ }
1515
1659
  // ── Notifications ───────────────────────────────────────────────
1516
1660
  /** Get notifications (replies, mentions, etc.). Returns a bare array. */
1517
1661
  async getNotifications(options = {}) {