@shivaedev/discord 0.3.0 → 0.4.1

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.
Files changed (85) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +6 -7
  3. package/dist/{gatewayConnection.d.ts → adapters/gatewayConnection.d.ts} +1 -1
  4. package/dist/adapters/gatewayConnection.d.ts.map +1 -0
  5. package/dist/adapters/gatewayConnection.js.map +1 -0
  6. package/dist/gateway/run.d.ts +6 -0
  7. package/dist/gateway/run.d.ts.map +1 -0
  8. package/dist/{runGateway.js → gateway/run.js} +3 -3
  9. package/dist/gateway/run.js.map +1 -0
  10. package/dist/{DiscordGateway.d.ts → gateway/service.d.ts} +2 -2
  11. package/dist/gateway/service.d.ts.map +1 -0
  12. package/dist/{DiscordGateway.js → gateway/service.js} +3 -3
  13. package/dist/gateway/service.js.map +1 -0
  14. package/dist/request.js +1 -1
  15. package/dist/request.js.map +1 -1
  16. package/dist/rest/initialize.d.ts +3 -0
  17. package/dist/rest/initialize.d.ts.map +1 -0
  18. package/dist/rest/initialize.js +3 -0
  19. package/dist/rest/initialize.js.map +1 -0
  20. package/dist/rest/layer.d.ts +3 -0
  21. package/dist/rest/layer.d.ts.map +1 -0
  22. package/dist/rest/layer.js +5 -0
  23. package/dist/rest/layer.js.map +1 -0
  24. package/dist/rest/request.d.ts +8 -0
  25. package/dist/rest/request.d.ts.map +1 -0
  26. package/dist/{restClient.js → rest/request.js} +12 -17
  27. package/dist/rest/request.js.map +1 -0
  28. package/dist/rest/service.d.ts +8 -0
  29. package/dist/rest/service.d.ts.map +1 -0
  30. package/dist/{DiscordRest.js → rest/service.js} +7 -5
  31. package/dist/rest/service.js.map +1 -0
  32. package/dist/rest-limits/initialize.d.ts +7 -0
  33. package/dist/rest-limits/initialize.d.ts.map +1 -0
  34. package/dist/rest-limits/initialize.js +9 -0
  35. package/dist/rest-limits/initialize.js.map +1 -0
  36. package/dist/rest-limits/observe.d.ts +5 -0
  37. package/dist/rest-limits/observe.d.ts.map +1 -0
  38. package/dist/rest-limits/observe.js +13 -0
  39. package/dist/rest-limits/observe.js.map +1 -0
  40. package/dist/rest-limits/refuse.d.ts +4 -0
  41. package/dist/rest-limits/refuse.d.ts.map +1 -0
  42. package/dist/rest-limits/refuse.js +13 -0
  43. package/dist/rest-limits/refuse.js.map +1 -0
  44. package/dist/rest-limits/service.d.ts +8 -0
  45. package/dist/rest-limits/service.d.ts.map +1 -0
  46. package/dist/rest-limits/service.js +12 -0
  47. package/dist/rest-limits/service.js.map +1 -0
  48. package/dist/rest-limits/wait.d.ts +4 -0
  49. package/dist/rest-limits/wait.d.ts.map +1 -0
  50. package/dist/rest-limits/wait.js +11 -0
  51. package/dist/rest-limits/wait.js.map +1 -0
  52. package/package.json +8 -8
  53. package/src/{runGateway.ts → gateway/run.ts} +2 -2
  54. package/src/{DiscordGateway.ts → gateway/service.ts} +2 -2
  55. package/src/request.ts +1 -1
  56. package/src/rest/initialize.ts +3 -0
  57. package/src/rest/layer.ts +5 -0
  58. package/src/{restClient.ts → rest/request.ts} +28 -35
  59. package/src/rest/service.ts +14 -0
  60. package/src/rest-limits/initialize.ts +9 -0
  61. package/src/rest-limits/observe.ts +18 -0
  62. package/src/rest-limits/refuse.ts +13 -0
  63. package/src/rest-limits/service.ts +12 -0
  64. package/src/rest-limits/wait.ts +12 -0
  65. package/dist/DiscordGateway.d.ts.map +0 -1
  66. package/dist/DiscordGateway.js.map +0 -1
  67. package/dist/DiscordRest.d.ts +0 -8
  68. package/dist/DiscordRest.d.ts.map +0 -1
  69. package/dist/DiscordRest.js.map +0 -1
  70. package/dist/gatewayConnection.d.ts.map +0 -1
  71. package/dist/gatewayConnection.js.map +0 -1
  72. package/dist/rateLimits.d.ts +0 -8
  73. package/dist/rateLimits.d.ts.map +0 -1
  74. package/dist/rateLimits.js +0 -36
  75. package/dist/rateLimits.js.map +0 -1
  76. package/dist/restClient.d.ts +0 -11
  77. package/dist/restClient.d.ts.map +0 -1
  78. package/dist/restClient.js.map +0 -1
  79. package/dist/runGateway.d.ts +0 -6
  80. package/dist/runGateway.d.ts.map +0 -1
  81. package/dist/runGateway.js.map +0 -1
  82. package/src/DiscordRest.ts +0 -12
  83. package/src/rateLimits.ts +0 -39
  84. /package/dist/{gatewayConnection.js → adapters/gatewayConnection.js} +0 -0
  85. /package/src/{gatewayConnection.ts → adapters/gatewayConnection.ts} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.1 - 2026-11-07
4
+
5
+ ### Changed
6
+
7
+ - Require Effect 4.1.1 and its matching platform packages.
8
+
9
+ ## 0.4.0 - 2026-11-05
10
+
11
+ ### Changed
12
+
13
+ - REST operations yield their declared dependencies; a dedicated rate-limit service owns pacing state for the registered Layer's lifetime. REST and gateway operations have named tracing spans.
14
+
15
+ ### Breaking
16
+
17
+ - Import `DiscordRest` from `@shivaedev/discord/rest/service.ts` and provide `layer` from `@shivaedev/discord/rest/layer.ts` in place of `DiscordRest.layer`. The composed Layer supplies the rate-limit service; continue supplying the native HTTP client, bot token and optional config once at registration.
18
+ - Import `DiscordGateway` from `@shivaedev/discord/gateway/service.ts`. Its Layer and `run(handler)` signature stay the same.
19
+ - Replace direct uses of the removed `restClient.ts` and `rateLimits.ts` factories with the composed REST Layer and `DiscordRest.request`. Direct gateway integrations import `run` from `gateway/run.ts` and `gatewayConnection` from `adapters/gatewayConnection.ts` instead of `runGateway.ts` and `gatewayConnection.ts`.
20
+
3
21
  ## 0.3.0 - 2026-11-05
4
22
 
5
23
  ### Breaking
package/README.md CHANGED
@@ -19,20 +19,19 @@ REST and gateway are independent services sharing a redacted `DiscordToken`. Pro
19
19
 
20
20
  ### Bot REST
21
21
 
22
- Provide `DiscordRest.layer`, a native `HttpClient` and the token:
22
+ Provide `layer` from `rest/layer.ts`, a native `HttpClient` and the token:
23
23
 
24
24
  ```ts
25
25
  import { Layer, Redacted } from "effect";
26
26
  import { FetchHttpClient } from "effect/http";
27
- import { DiscordRest } from "@shivaedev/discord/DiscordRest.ts";
27
+ import { layer } from "@shivaedev/discord/rest/layer.ts";
28
28
  import { DiscordToken } from "@shivaedev/discord/DiscordToken.ts";
29
29
 
30
- const restLayer = DiscordRest.layer.pipe(
31
- Layer.provide(FetchHttpClient.layer),
32
- Layer.provide(Layer.succeed(DiscordToken)(Redacted.make("bot-token"))),
33
- );
30
+ const restLayer = layer.pipe(Layer.provide(FetchHttpClient.layer), Layer.provide(Layer.succeed(DiscordToken)(Redacted.make("bot-token"))));
34
31
  ```
35
32
 
33
+ The composed REST Layer provides `DiscordRest` from `rest/service.ts` and its `DiscordRestLimits` dependency. Its registration chooses their lifetime: share one build for a bot, or build a fresh Layer for independent pacing state. Operations yield the HTTP client, token, config and limits from the environment; REST initializer state owns only a semaphore, and the limits service owns the bucket maps and reset times.
34
+
36
35
  `request(method, path, schema, body?)` reads the service and decodes the result. `DiscordRest.request(method, path, body?)` returns decoded JSON as `unknown`; a 204 response returns `undefined`. Paths start with `/` and are relative to Discord's API v10 base. Request bodies use native HTTP JSON encoding, and response reads use `@shivaedev/http` with a 1 MiB limit.
37
36
 
38
37
  One service instance serializes calls for its bot. It tracks Discord's bucket ids, major channel/guild identifiers, remaining count and relative reset time. An exhausted bucket delays the next matching call. A 429 reads Discord's `retry_after` seconds and `global` flag, releases the response, waits on Effect's clock and retries up to three times. Exhaustion fails with `DiscordRateLimited`, carrying the final delay in seconds. Transport failures become `DiscordTransportError`; other HTTP statuses retain `@shivaedev/http`'s typed failures. Schema decoding failures remain in the error channel.
@@ -43,7 +42,7 @@ Provide `DiscordGateway.layer`, the same token, `DiscordConfig` with your intent
43
42
 
44
43
  ```ts
45
44
  import { Effect } from "effect";
46
- import { DiscordGateway } from "@shivaedev/discord/DiscordGateway.ts";
45
+ import { DiscordGateway } from "@shivaedev/discord/gateway/service.ts";
47
46
 
48
47
  const listen = Effect.gen(function* () {
49
48
  const gateway = yield* DiscordGateway;
@@ -2,5 +2,5 @@ import { Effect } from "effect";
2
2
  import { Socket } from "effect/socket";
3
3
  import { DiscordToken } from "#DiscordToken.ts";
4
4
  import { type GatewayDispatch, type GatewaySession } from "#gatewayProtocol.ts";
5
- export declare function gatewayConnection(session: GatewaySession, dispatch: (event: GatewayDispatch) => Effect.Effect<void>): Effect.Effect<never, import("./errors.ts").DiscordGatewayProtocolError | Socket.SocketError, DiscordToken | Socket.Socket>;
5
+ export declare function gatewayConnection(session: GatewaySession, dispatch: (event: GatewayDispatch) => Effect.Effect<void>): Effect.Effect<never, import("../errors.ts").DiscordGatewayProtocolError | Socket.SocketError, DiscordToken | Socket.Socket>;
6
6
  //# sourceMappingURL=gatewayConnection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gatewayConnection.d.ts","sourceRoot":"","sources":["../../src/adapters/gatewayConnection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,MAAM,EAAoB,MAAM,QAAQ,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAEvC,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAA2B,KAAK,eAAe,EAAqB,KAAK,cAAc,EAA2B,MAAM,qBAAqB,CAAC;AAErJ,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,+HAkFnH"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gatewayConnection.js","sourceRoot":"","sources":["../../src/adapters/gatewayConnection.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { Effect } from "effect";
2
+ import { DiscordGatewayClosed } from "#errors.ts";
3
+ import { GatewayTransport } from "#GatewayTransport.ts";
4
+ import type { GatewayDispatch } from "#gatewayProtocol.ts";
5
+ export declare const run: (dispatch: (event: GatewayDispatch) => Effect.Effect<void>) => Effect.Effect<never, DiscordGatewayClosed | import("#errors.ts").DiscordGatewayProtocolError, import("../DiscordToken.ts").DiscordToken | GatewayTransport>;
6
+ //# sourceMappingURL=run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/gateway/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAIhC,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,eAAe,EAAkB,MAAM,qBAAqB,CAAC;AAE3E,eAAO,MAAM,GAAG,qBAAgE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,gKAuBrH,CAAC"}
@@ -1,10 +1,10 @@
1
1
  import { Effect } from "effect";
2
2
  import { Socket } from "effect/socket";
3
+ import { gatewayConnection } from "#adapters/gatewayConnection.ts";
3
4
  import { DiscordConfig } from "#DiscordConfig.ts";
4
5
  import { DiscordGatewayClosed } from "#errors.ts";
5
6
  import { GatewayTransport } from "#GatewayTransport.ts";
6
- import { gatewayConnection } from "#gatewayConnection.ts";
7
- export const runGateway = Effect.fnUntraced(function* (dispatch) {
7
+ export const run = Effect.fn("DiscordGateway.run")(function* (dispatch) {
8
8
  const config = yield* DiscordConfig;
9
9
  const transport = yield* GatewayTransport;
10
10
  const session = { resumeUrl: undefined, sequence: null, sessionId: undefined };
@@ -26,4 +26,4 @@ export const runGateway = Effect.fnUntraced(function* (dispatch) {
26
26
  }));
27
27
  }
28
28
  });
29
- //# sourceMappingURL=runGateway.js.map
29
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/gateway/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAGxD,MAAM,CAAC,MAAM,GAAG,GAAG,MAAM,CAAC,EAAE,CAAC,oBAAoB,CAAC,CAAC,QAAQ,CAAC,EAAE,QAAyD;IACtH,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,aAAa,CAAC;IACpC,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,gBAAgB,CAAC;IAC1C,MAAM,OAAO,GAAmB,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;IAC/F,OAAO,IAAI,EAAE,CAAC;QACb,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,SAAS,IAAI,MAAM,CAAC,UAAU,sBAAsB,CAAC,CAAC;QACtG,KAAK,CAAC,CAAC,iBAAiB,CAAC,OAAO,EAAE,QAAQ,CAAC;aACzC,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;aAClD,IAAI,CACJ,MAAM,CAAC,QAAQ,CAAC,aAAa,EAAE,CAAC,KAAK,EAAE,EAAE;YACxC,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,KAAK,kBAAkB,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;YACtF,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,EAAE,CAAC;gBAC3E,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,oBAAoB,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YACxD,CAAC;YACD,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;gBACpC,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;gBAC9B,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;gBAC9B,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;YACzB,CAAC;YACD,OAAO,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QACjC,CAAC,CAAC,CACF,CAAC;IACJ,CAAC;AACF,CAAC,CAAC,CAAC"}
@@ -2,8 +2,8 @@ import { Effect } from "effect";
2
2
  import { DiscordToken } from "#DiscordToken.ts";
3
3
  import { GatewayTransport } from "#GatewayTransport.ts";
4
4
  export declare const DiscordGateway: import("effect/Context").Service<"@shivaedev/discord/DiscordGateway", Readonly<{
5
- readonly run: (dispatch: (event: import("./gatewayProtocol.ts").GatewayDispatch) => Effect.Effect<void>) => Effect.Effect<never, import("./errors.ts").DiscordGatewayClosed | import("./errors.ts").DiscordGatewayProtocolError, never>;
5
+ readonly run: (dispatch: (event: import("../gatewayProtocol.ts").GatewayDispatch) => Effect.Effect<void>) => Effect.Effect<never, import("../errors.ts").DiscordGatewayClosed | import("../errors.ts").DiscordGatewayProtocolError, never>;
6
6
  }>> & {
7
7
  readonly layer: import("effect/Layer").Layer<"@shivaedev/discord/DiscordGateway", never, DiscordToken | GatewayTransport>;
8
8
  };
9
- //# sourceMappingURL=DiscordGateway.d.ts.map
9
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/gateway/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGhC,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAGxD,eAAO,MAAM,cAAc;;;;CAKzB,CAAC"}
@@ -3,11 +3,11 @@ import { defineService } from "@shivaedev/service/defineService.ts";
3
3
  import { DiscordConfig } from "#DiscordConfig.ts";
4
4
  import { DiscordToken } from "#DiscordToken.ts";
5
5
  import { GatewayTransport } from "#GatewayTransport.ts";
6
- import { runGateway } from "#runGateway.ts";
6
+ import { run } from "#gateway/run.ts";
7
7
  export const DiscordGateway = defineService({
8
8
  id: "@shivaedev/discord/DiscordGateway",
9
9
  initialize: Effect.void,
10
- methods: () => ({ run: runGateway }),
10
+ methods: () => ({ run }),
11
11
  requires: [DiscordConfig, DiscordToken, GatewayTransport],
12
12
  });
13
- //# sourceMappingURL=DiscordGateway.js.map
13
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/gateway/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAEtC,MAAM,CAAC,MAAM,cAAc,GAAG,aAAa,CAAC;IAC3C,EAAE,EAAE,mCAAmC;IACvC,UAAU,EAAE,MAAM,CAAC,IAAI;IACvB,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC;IACxB,QAAQ,EAAE,CAAC,aAAa,EAAE,YAAY,EAAE,gBAAgB,CAAC;CACzD,CAAC,CAAC"}
package/dist/request.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Effect, Schema } from "effect";
2
- import { DiscordRest } from "#DiscordRest.ts";
2
+ import { DiscordRest } from "#rest/service.ts";
3
3
  export function request(method, path, schema, body) {
4
4
  return Effect.flatMap(DiscordRest, (rest) => rest.request(method, path, body)).pipe(Effect.flatMap(Schema.decodeUnknownEffect(schema)));
5
5
  }
@@ -1 +1 @@
1
- {"version":3,"file":"request.js","sourceRoot":"","sources":["../src/request.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAExC,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE9C,MAAM,UAAU,OAAO,CAAoD,MAAkB,EAAE,IAAY,EAAE,MAAe,EAAE,IAAc;IAC3I,OAAO,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;AACzI,CAAC"}
1
+ {"version":3,"file":"request.js","sourceRoot":"","sources":["../src/request.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAExC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,MAAM,UAAU,OAAO,CAAoD,MAAkB,EAAE,IAAY,EAAE,MAAe,EAAE,IAAc;IAC3I,OAAO,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;AACzI,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Semaphore } from "effect";
2
+ export declare const initialize: import("effect/Effect").Effect<Semaphore.Semaphore, never, never>;
3
+ //# sourceMappingURL=initialize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"initialize.d.ts","sourceRoot":"","sources":["../../src/rest/initialize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAEnC,eAAO,MAAM,UAAU,mEAAoB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Semaphore } from "effect";
2
+ export const initialize = Semaphore.make(1);
3
+ //# sourceMappingURL=initialize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"initialize.js","sourceRoot":"","sources":["../../src/rest/initialize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAEnC,MAAM,CAAC,MAAM,UAAU,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Layer } from "effect";
2
+ export declare const layer: Layer.Layer<"@shivaedev/discord/DiscordRest", never, import("../DiscordToken.ts").DiscordToken | import("effect/http/HttpClient").HttpClient>;
3
+ //# sourceMappingURL=layer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layer.d.ts","sourceRoot":"","sources":["../../src/rest/layer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAI/B,eAAO,MAAM,KAAK,+IAAiE,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { Layer } from "effect";
2
+ import { DiscordRest } from "#rest/service.ts";
3
+ import { DiscordRestLimits } from "#rest-limits/service.ts";
4
+ export const layer = DiscordRest.layer.pipe(Layer.provide(DiscordRestLimits.layer));
5
+ //# sourceMappingURL=layer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layer.js","sourceRoot":"","sources":["../../src/rest/layer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAC/B,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAE5D,MAAM,CAAC,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { Effect, Schema } from "effect";
2
+ import { HttpClient } from "effect/http";
3
+ import type { HttpMethod } from "effect/http/HttpMethod";
4
+ import { DiscordToken } from "#DiscordToken.ts";
5
+ import { DiscordRateLimited, DiscordTransportError } from "#errors.ts";
6
+ import type { initialize } from "#rest/initialize.ts";
7
+ export declare function request(lock: Effect.Success<typeof initialize>): (method: HttpMethod, path: string, body?: unknown) => Effect.Effect<unknown, DiscordRateLimited | DiscordTransportError | import("effect/http/HttpBody").HttpBodyError | import("@shivaedev/http/errors.ts").InvalidBodyLimit | import("@shivaedev/http/errors.ts").RemoteGone | import("@shivaedev/http/errors.ts").RemoteHttpError | import("@shivaedev/http/errors.ts").RemoteNotFound | import("@shivaedev/http/errors.ts").RemoteRateLimited | import("@shivaedev/http/errors.ts").RemoteUnauthorized | import("@shivaedev/http/errors.ts").ResponseBodyTooLarge | Schema.SchemaError, "@shivaedev/discord/DiscordRestLimits" | DiscordToken | HttpClient.HttpClient>;
8
+ //# sourceMappingURL=request.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../../src/rest/request.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAY,MAAM,EAAE,MAAM,QAAQ,CAAC;AAClD,OAAO,EAAE,UAAU,EAAqB,MAAM,aAAa,CAAC;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAMzD,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACvE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAOtD,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO,UAAU,CAAC,8oBAuC9D"}
@@ -1,4 +1,4 @@
1
- import { Effect, Redacted, Schema, Semaphore } from "effect";
1
+ import { Effect, Redacted, Schema } from "effect";
2
2
  import { HttpClient, HttpClientRequest } from "effect/http";
3
3
  import { readBoundedJson } from "@shivaedev/http/body.ts";
4
4
  import { checkStatus } from "@shivaedev/http/checkStatus.ts";
@@ -7,15 +7,14 @@ import { routeKey } from "#adapters/routeKey.ts";
7
7
  import { DiscordConfig } from "#DiscordConfig.ts";
8
8
  import { DiscordToken } from "#DiscordToken.ts";
9
9
  import { DiscordRateLimited, DiscordTransportError } from "#errors.ts";
10
- import { rateLimits } from "#rateLimits.ts";
10
+ import { DiscordRestLimits } from "#rest-limits/service.ts";
11
11
  const Limited = Schema.Struct({ global: Schema.optional(Schema.Boolean), retryAfter: Schema.Number }).pipe(Schema.encodeKeys({ retryAfter: "retry_after" }));
12
- export const initializeRest = Effect.gen(function* () {
13
- const client = HttpClient.withScope(yield* HttpClient.HttpClient);
14
- const token = yield* DiscordToken;
15
- const config = yield* DiscordConfig;
16
- const rates = rateLimits();
17
- const lock = yield* Semaphore.make(1);
18
- const request = Effect.fnUntraced(function* (method, path, body) {
12
+ export function request(lock) {
13
+ return Effect.fn("DiscordRest.request")(function* (method, path, body) {
14
+ const client = HttpClient.withScope(yield* HttpClient.HttpClient);
15
+ const token = yield* DiscordToken;
16
+ const config = yield* DiscordConfig;
17
+ const rates = yield* DiscordRestLimits;
19
18
  const { major, route } = routeKey(method, path);
20
19
  let input = HttpClientRequest.make(method)(`${config.apiUrl}${path}`).pipe(HttpClientRequest.setHeader("Authorization", `Bot ${Redacted.value(token)}`));
21
20
  if (body !== undefined) {
@@ -23,7 +22,7 @@ export const initializeRest = Effect.gen(function* () {
23
22
  }
24
23
  for (let attempt = 0;; attempt += 1) {
25
24
  yield* rates.wait(route, major);
26
- const answer = yield* Effect.scoped(Effect.gen(function* () {
25
+ const answer = yield* Effect.gen(function* () {
27
26
  const response = yield* client.execute(input);
28
27
  yield* rates.observe(route, major, response.headers);
29
28
  if (isRateLimited(response.status)) {
@@ -37,15 +36,11 @@ export const initializeRest = Effect.gen(function* () {
37
36
  yield* checkStatus(response);
38
37
  const value = isEmpty(response.status) ? undefined : yield* readBoundedJson(response, 1024 * 1024, Schema.Unknown);
39
38
  return { retry: false, value };
40
- }));
39
+ }).pipe(Effect.scoped);
41
40
  if (!answer.retry) {
42
41
  return answer.value;
43
42
  }
44
43
  }
45
- });
46
- return { lock, request };
47
- });
48
- export function restRequest(state) {
49
- return (method, path, body) => state.request(method, path, body).pipe(state.lock.withPermit, Effect.catchTag("HttpClientError", () => Effect.fail(new DiscordTransportError({}))));
44
+ }, lock.withPermit, Effect.catchTag("HttpClientError", () => Effect.fail(new DiscordTransportError({}))));
50
45
  }
51
- //# sourceMappingURL=restClient.js.map
46
+ //# sourceMappingURL=request.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request.js","sourceRoot":"","sources":["../../src/rest/request.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAE5D,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAC7D,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAEvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAE5D,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CACzG,MAAM,CAAC,UAAU,CAAC,EAAE,UAAU,EAAE,aAAa,EAAE,CAAC,CAChD,CAAC;AAEF,MAAM,UAAU,OAAO,CAAC,IAAuC;IAC9D,OAAO,MAAM,CAAC,EAAE,CAAC,qBAAqB,CAAC,CACtC,QAAQ,CAAC,EAAE,MAAkB,EAAE,IAAY,EAAE,IAAc;QAC1D,MAAM,MAAM,GAAG,UAAU,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;QAClE,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,YAAY,CAAC;QAClC,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,aAAa,CAAC;QACpC,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,iBAAiB,CAAC;QACvC,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAChD,IAAI,KAAK,GAAG,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,IAAI,EAAE,CAAC,CAAC,IAAI,CACzE,iBAAiB,CAAC,SAAS,CAAC,eAAe,EAAE,OAAO,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAC5E,CAAC;QACF,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACxB,KAAK,GAAG,KAAK,CAAC,CAAC,iBAAiB,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACxD,CAAC;QACD,KAAK,IAAI,OAAO,GAAG,CAAC,GAAI,OAAO,IAAI,CAAC,EAAE,CAAC;YACtC,KAAK,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YAChC,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;gBACzC,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;gBAC9C,KAAK,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;gBACrD,IAAI,aAAa,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;oBACpC,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,eAAe,CAAC,QAAQ,EAAE,EAAE,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;oBACnE,KAAK,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC;oBAC3E,IAAI,OAAO,KAAK,CAAC,EAAE,CAAC;wBACnB,OAAO,KAAK,CAAC,CAAC,IAAI,kBAAkB,CAAC,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;oBACxE,CAAC;oBACD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;gBAC1C,CAAC;gBACD,KAAK,CAAC,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;gBAC7B,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,eAAe,CAAC,QAAQ,EAAE,IAAI,GAAG,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;gBACnH,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;YAChC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACvB,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;gBACnB,OAAO,MAAM,CAAC,KAAK,CAAC;YACrB,CAAC;QACF,CAAC;IACF,CAAC,EACD,IAAI,CAAC,UAAU,EACf,MAAM,CAAC,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,qBAAqB,CAAC,EAAE,CAAC,CAAC,CAAC,CACpF,CAAC;AACH,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { HttpClient } from "effect/http";
2
+ import { DiscordToken } from "#DiscordToken.ts";
3
+ export declare const DiscordRest: import("effect/Context").Service<"@shivaedev/discord/DiscordRest", Readonly<{
4
+ readonly request: (method: import("effect/http/HttpMethod").HttpMethod, path: string, body?: unknown) => import("effect/Effect").Effect<unknown, import("../errors.ts").DiscordRateLimited | import("../errors.ts").DiscordTransportError | import("effect/http/HttpBody").HttpBodyError | import("@shivaedev/http/errors.ts").InvalidBodyLimit | import("@shivaedev/http/errors.ts").RemoteGone | import("@shivaedev/http/errors.ts").RemoteHttpError | import("@shivaedev/http/errors.ts").RemoteNotFound | import("@shivaedev/http/errors.ts").RemoteRateLimited | import("@shivaedev/http/errors.ts").RemoteUnauthorized | import("@shivaedev/http/errors.ts").ResponseBodyTooLarge | import("effect/Schema").SchemaError, never>;
5
+ }>> & {
6
+ readonly layer: import("effect/Layer").Layer<"@shivaedev/discord/DiscordRest", never, "@shivaedev/discord/DiscordRestLimits" | DiscordToken | HttpClient.HttpClient>;
7
+ };
8
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/rest/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGzC,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAKhD,eAAO,MAAM,WAAW;;;;CAKtB,CAAC"}
@@ -2,11 +2,13 @@ import { HttpClient } from "effect/http";
2
2
  import { defineService } from "@shivaedev/service/defineService.ts";
3
3
  import { DiscordConfig } from "#DiscordConfig.ts";
4
4
  import { DiscordToken } from "#DiscordToken.ts";
5
- import { initializeRest, restRequest } from "#restClient.ts";
5
+ import { initialize } from "#rest/initialize.ts";
6
+ import { request } from "#rest/request.ts";
7
+ import { DiscordRestLimits } from "#rest-limits/service.ts";
6
8
  export const DiscordRest = defineService({
7
9
  id: "@shivaedev/discord/DiscordRest",
8
- initialize: initializeRest,
9
- methods: (state) => ({ request: restRequest(state) }),
10
- requires: [HttpClient.HttpClient, DiscordToken, DiscordConfig],
10
+ initialize,
11
+ methods: (state) => ({ request: request(state) }),
12
+ requires: [HttpClient.HttpClient, DiscordToken, DiscordConfig, DiscordRestLimits],
11
13
  });
12
- //# sourceMappingURL=DiscordRest.js.map
14
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/rest/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAE5D,MAAM,CAAC,MAAM,WAAW,GAAG,aAAa,CAAC;IACxC,EAAE,EAAE,gCAAgC;IACpC,UAAU;IACV,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;IACjD,QAAQ,EAAE,CAAC,UAAU,CAAC,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,iBAAiB,CAAC;CACjF,CAAC,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { Effect, Ref } from "effect";
2
+ export declare const initialize: Effect.Effect<{
3
+ blocked: Map<string, number>;
4
+ globalReset: Ref.Ref<number>;
5
+ routes: Map<string, string>;
6
+ }, never, never>;
7
+ //# sourceMappingURL=initialize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"initialize.d.ts","sourceRoot":"","sources":["../../src/rest-limits/initialize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC;AAErC,eAAO,MAAM,UAAU;;;;gBAMrB,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { Effect, Ref } from "effect";
2
+ export const initialize = Effect.gen(function* () {
3
+ return {
4
+ blocked: new Map(),
5
+ globalReset: yield* Ref.make(0),
6
+ routes: new Map(),
7
+ };
8
+ });
9
+ //# sourceMappingURL=initialize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"initialize.js","sourceRoot":"","sources":["../../src/rest-limits/initialize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC;AAErC,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAC7C,OAAO;QACN,OAAO,EAAE,IAAI,GAAG,EAAkB;QAClC,WAAW,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;QAC/B,MAAM,EAAE,IAAI,GAAG,EAAkB;KACjC,CAAC;AACH,CAAC,CAAC,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { Effect } from "effect";
2
+ import type { Headers } from "effect/http";
3
+ import type { initialize } from "#rest-limits/initialize.ts";
4
+ export declare function observe(state: Effect.Success<typeof initialize>): (route: string, major: string, headers: Headers.Headers) => Effect.Effect<void, never, never>;
5
+ //# sourceMappingURL=observe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observe.d.ts","sourceRoot":"","sources":["../../src/rest-limits/observe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,MAAM,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAE7D,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO,UAAU,CAAC,iGAa/D"}
@@ -0,0 +1,13 @@
1
+ import { Clock, Effect } from "effect";
2
+ export function observe(state) {
3
+ return Effect.fn("DiscordRestLimits.observe")(function* (route, major, headers) {
4
+ const bucket = headers["x-ratelimit-bucket"];
5
+ if (bucket !== undefined) {
6
+ state.routes.set(route, bucket);
7
+ }
8
+ if (headers["x-ratelimit-remaining"] === "0") {
9
+ state.blocked.set(`${state.routes.get(route) ?? route}:${major}`, (yield* Clock.currentTimeMillis) + Number(headers["x-ratelimit-reset-after"] ?? 0) * 1000);
10
+ }
11
+ });
12
+ }
13
+ //# sourceMappingURL=observe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observe.js","sourceRoot":"","sources":["../../src/rest-limits/observe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAIvC,MAAM,UAAU,OAAO,CAAC,KAAwC;IAC/D,OAAO,MAAM,CAAC,EAAE,CAAC,2BAA2B,CAAC,CAAC,QAAQ,CAAC,EAAE,KAAa,EAAE,KAAa,EAAE,OAAwB;QAC9G,MAAM,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;QAC7C,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YAC1B,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QACjC,CAAC;QACD,IAAI,OAAO,CAAC,uBAAuB,CAAC,KAAK,GAAG,EAAE,CAAC;YAC9C,KAAK,CAAC,OAAO,CAAC,GAAG,CAChB,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,KAAK,EAAE,EAC9C,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,yBAAyB,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CACzF,CAAC;QACH,CAAC;IACF,CAAC,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { Effect } from "effect";
2
+ import type { initialize } from "#rest-limits/initialize.ts";
3
+ export declare function refuse(state: Effect.Success<typeof initialize>): (route: string, major: string, seconds: number, global: boolean) => Effect.Effect<void, never, never>;
4
+ //# sourceMappingURL=refuse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refuse.d.ts","sourceRoot":"","sources":["../../src/rest-limits/refuse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,MAAM,EAAO,MAAM,QAAQ,CAAC;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAE7D,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO,UAAU,CAAC,yGAS9D"}
@@ -0,0 +1,13 @@
1
+ import { Clock, Effect, Ref } from "effect";
2
+ export function refuse(state) {
3
+ return Effect.fn("DiscordRestLimits.refuse")(function* (route, major, seconds, global) {
4
+ const reset = (yield* Clock.currentTimeMillis) + seconds * 1000;
5
+ if (global) {
6
+ yield* Ref.update(state.globalReset, (previous) => Math.max(previous, reset));
7
+ }
8
+ else {
9
+ state.blocked.set(`${state.routes.get(route) ?? route}:${major}`, reset);
10
+ }
11
+ });
12
+ }
13
+ //# sourceMappingURL=refuse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refuse.js","sourceRoot":"","sources":["../../src/rest-limits/refuse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC;AAG5C,MAAM,UAAU,MAAM,CAAC,KAAwC;IAC9D,OAAO,MAAM,CAAC,EAAE,CAAC,0BAA0B,CAAC,CAAC,QAAQ,CAAC,EAAE,KAAa,EAAE,KAAa,EAAE,OAAe,EAAE,MAAe;QACrH,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,GAAG,OAAO,GAAG,IAAI,CAAC;QAChE,IAAI,MAAM,EAAE,CAAC;YACZ,KAAK,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC;QAC/E,CAAC;aAAM,CAAC;YACP,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,KAAK,EAAE,EAAE,KAAK,CAAC,CAAC;QAC1E,CAAC;IACF,CAAC,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,8 @@
1
+ export declare const DiscordRestLimits: import("effect/Context").Service<"@shivaedev/discord/DiscordRestLimits", Readonly<{
2
+ readonly observe: (route: string, major: string, headers: import("effect/http/Headers").Headers) => import("effect/Effect").Effect<void, never, never>;
3
+ readonly refuse: (route: string, major: string, seconds: number, global: boolean) => import("effect/Effect").Effect<void, never, never>;
4
+ readonly wait: (route: string, major: string) => import("effect/Effect").Effect<void, never, never>;
5
+ }>> & {
6
+ readonly layer: import("effect/Layer").Layer<"@shivaedev/discord/DiscordRestLimits", never, never>;
7
+ };
8
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/rest-limits/service.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,iBAAiB;;;;;;CAK5B,CAAC"}
@@ -0,0 +1,12 @@
1
+ import { defineService } from "@shivaedev/service/defineService.ts";
2
+ import { initialize } from "#rest-limits/initialize.ts";
3
+ import { observe } from "#rest-limits/observe.ts";
4
+ import { refuse } from "#rest-limits/refuse.ts";
5
+ import { wait } from "#rest-limits/wait.ts";
6
+ export const DiscordRestLimits = defineService({
7
+ id: "@shivaedev/discord/DiscordRestLimits",
8
+ initialize,
9
+ methods: (state) => ({ observe: observe(state), refuse: refuse(state), wait: wait(state) }),
10
+ requires: [],
11
+ });
12
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/rest-limits/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAE5C,MAAM,CAAC,MAAM,iBAAiB,GAAG,aAAa,CAAC;IAC9C,EAAE,EAAE,sCAAsC;IAC1C,UAAU;IACV,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;IAC3F,QAAQ,EAAE,EAAE;CACZ,CAAC,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { Effect } from "effect";
2
+ import type { initialize } from "#rest-limits/initialize.ts";
3
+ export declare function wait(state: Effect.Success<typeof initialize>): (route: string, major: string) => Effect.Effect<void, never, never>;
4
+ //# sourceMappingURL=wait.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wait.d.ts","sourceRoot":"","sources":["../../src/rest-limits/wait.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,MAAM,EAAO,MAAM,QAAQ,CAAC;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAE7D,wBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO,UAAU,CAAC,uEAQ5D"}
@@ -0,0 +1,11 @@
1
+ import { Clock, Effect, Ref } from "effect";
2
+ export function wait(state) {
3
+ return Effect.fn("DiscordRestLimits.wait")(function* (route, major) {
4
+ const reset = Math.max(yield* Ref.get(state.globalReset), state.blocked.get(`${state.routes.get(route) ?? route}:${major}`) ?? 0);
5
+ const delay = reset - (yield* Clock.currentTimeMillis);
6
+ if (delay > 0) {
7
+ yield* Effect.sleep(delay);
8
+ }
9
+ });
10
+ }
11
+ //# sourceMappingURL=wait.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wait.js","sourceRoot":"","sources":["../../src/rest-limits/wait.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC;AAG5C,MAAM,UAAU,IAAI,CAAC,KAAwC;IAC5D,OAAO,MAAM,CAAC,EAAE,CAAC,wBAAwB,CAAC,CAAC,QAAQ,CAAC,EAAE,KAAa,EAAE,KAAa;QACjF,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;QAClI,MAAM,KAAK,GAAG,KAAK,GAAG,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QACvD,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YACf,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC5B,CAAC;IACF,CAAC,CAAC,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shivaedev/discord",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "Discord integrations for Effect applications",
5
5
  "homepage": "https://github.com/ShivaeDev/platform/tree/main/packages/discord#readme",
6
6
  "bugs": {
@@ -40,20 +40,20 @@
40
40
  "README.md"
41
41
  ],
42
42
  "dependencies": {
43
- "@shivaedev/http": "0.8.0",
44
- "@shivaedev/service": "0.8.0"
43
+ "@shivaedev/http": "0.8.1",
44
+ "@shivaedev/service": "0.8.1"
45
45
  },
46
46
  "devDependencies": {
47
- "@shivaedev/quality": "0.65.0",
48
- "@shivaedev/test-kits": "0.8.0",
49
- "@shivaedev/test-story": "0.42.0",
47
+ "@shivaedev/quality": "0.70.1",
48
+ "@shivaedev/test-kits": "0.10.2",
49
+ "@shivaedev/test-story": "0.45.2",
50
50
  "@types/node": "24.19.2",
51
- "effect": "4.1.0",
51
+ "effect": "4.1.1",
52
52
  "typescript": "7.0.2",
53
53
  "vitest": "5.0.5"
54
54
  },
55
55
  "peerDependencies": {
56
- "effect": "4.1.0"
56
+ "effect": "4.1.1"
57
57
  },
58
58
  "engines": {
59
59
  "node": ">=24"
@@ -1,12 +1,12 @@
1
1
  import { Effect } from "effect";
2
2
  import { Socket } from "effect/socket";
3
+ import { gatewayConnection } from "#adapters/gatewayConnection.ts";
3
4
  import { DiscordConfig } from "#DiscordConfig.ts";
4
5
  import { DiscordGatewayClosed } from "#errors.ts";
5
6
  import { GatewayTransport } from "#GatewayTransport.ts";
6
- import { gatewayConnection } from "#gatewayConnection.ts";
7
7
  import type { GatewayDispatch, GatewaySession } from "#gatewayProtocol.ts";
8
8
 
9
- export const runGateway = Effect.fnUntraced(function* (dispatch: (event: GatewayDispatch) => Effect.Effect<void>) {
9
+ export const run = Effect.fn("DiscordGateway.run")(function* (dispatch: (event: GatewayDispatch) => Effect.Effect<void>) {
10
10
  const config = yield* DiscordConfig;
11
11
  const transport = yield* GatewayTransport;
12
12
  const session: GatewaySession = { resumeUrl: undefined, sequence: null, sessionId: undefined };
@@ -3,11 +3,11 @@ import { defineService } from "@shivaedev/service/defineService.ts";
3
3
  import { DiscordConfig } from "#DiscordConfig.ts";
4
4
  import { DiscordToken } from "#DiscordToken.ts";
5
5
  import { GatewayTransport } from "#GatewayTransport.ts";
6
- import { runGateway } from "#runGateway.ts";
6
+ import { run } from "#gateway/run.ts";
7
7
 
8
8
  export const DiscordGateway = defineService({
9
9
  id: "@shivaedev/discord/DiscordGateway",
10
10
  initialize: Effect.void,
11
- methods: () => ({ run: runGateway }),
11
+ methods: () => ({ run }),
12
12
  requires: [DiscordConfig, DiscordToken, GatewayTransport],
13
13
  });
package/src/request.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Effect, Schema } from "effect";
2
2
  import type { HttpMethod } from "effect/http/HttpMethod";
3
- import { DiscordRest } from "#DiscordRest.ts";
3
+ import { DiscordRest } from "#rest/service.ts";
4
4
 
5
5
  export function request<TSchema extends Schema.ConstraintDecoder<unknown>>(method: HttpMethod, path: string, schema: TSchema, body?: unknown) {
6
6
  return Effect.flatMap(DiscordRest, (rest) => rest.request(method, path, body)).pipe(Effect.flatMap(Schema.decodeUnknownEffect(schema)));
@@ -0,0 +1,3 @@
1
+ import { Semaphore } from "effect";
2
+
3
+ export const initialize = Semaphore.make(1);
@@ -0,0 +1,5 @@
1
+ import { Layer } from "effect";
2
+ import { DiscordRest } from "#rest/service.ts";
3
+ import { DiscordRestLimits } from "#rest-limits/service.ts";
4
+
5
+ export const layer = DiscordRest.layer.pipe(Layer.provide(DiscordRestLimits.layer));
@@ -1,4 +1,4 @@
1
- import { Effect, Redacted, Schema, Semaphore } from "effect";
1
+ import { Effect, Redacted, Schema } from "effect";
2
2
  import { HttpClient, HttpClientRequest } from "effect/http";
3
3
  import type { HttpMethod } from "effect/http/HttpMethod";
4
4
  import { readBoundedJson } from "@shivaedev/http/body.ts";
@@ -8,30 +8,30 @@ import { routeKey } from "#adapters/routeKey.ts";
8
8
  import { DiscordConfig } from "#DiscordConfig.ts";
9
9
  import { DiscordToken } from "#DiscordToken.ts";
10
10
  import { DiscordRateLimited, DiscordTransportError } from "#errors.ts";
11
- import { rateLimits } from "#rateLimits.ts";
11
+ import type { initialize } from "#rest/initialize.ts";
12
+ import { DiscordRestLimits } from "#rest-limits/service.ts";
12
13
 
13
14
  const Limited = Schema.Struct({ global: Schema.optional(Schema.Boolean), retryAfter: Schema.Number }).pipe(
14
15
  Schema.encodeKeys({ retryAfter: "retry_after" }),
15
16
  );
16
17
 
17
- export const initializeRest = Effect.gen(function* () {
18
- const client = HttpClient.withScope(yield* HttpClient.HttpClient);
19
- const token = yield* DiscordToken;
20
- const config = yield* DiscordConfig;
21
- const rates = rateLimits();
22
- const lock = yield* Semaphore.make(1);
23
- const request = Effect.fnUntraced(function* (method: HttpMethod, path: string, body?: unknown) {
24
- const { major, route } = routeKey(method, path);
25
- let input = HttpClientRequest.make(method)(`${config.apiUrl}${path}`).pipe(
26
- HttpClientRequest.setHeader("Authorization", `Bot ${Redacted.value(token)}`),
27
- );
28
- if (body !== undefined) {
29
- input = yield* HttpClientRequest.bodyJson(input, body);
30
- }
31
- for (let attempt = 0; ; attempt += 1) {
32
- yield* rates.wait(route, major);
33
- const answer = yield* Effect.scoped(
34
- Effect.gen(function* () {
18
+ export function request(lock: Effect.Success<typeof initialize>) {
19
+ return Effect.fn("DiscordRest.request")(
20
+ function* (method: HttpMethod, path: string, body?: unknown) {
21
+ const client = HttpClient.withScope(yield* HttpClient.HttpClient);
22
+ const token = yield* DiscordToken;
23
+ const config = yield* DiscordConfig;
24
+ const rates = yield* DiscordRestLimits;
25
+ const { major, route } = routeKey(method, path);
26
+ let input = HttpClientRequest.make(method)(`${config.apiUrl}${path}`).pipe(
27
+ HttpClientRequest.setHeader("Authorization", `Bot ${Redacted.value(token)}`),
28
+ );
29
+ if (body !== undefined) {
30
+ input = yield* HttpClientRequest.bodyJson(input, body);
31
+ }
32
+ for (let attempt = 0; ; attempt += 1) {
33
+ yield* rates.wait(route, major);
34
+ const answer = yield* Effect.gen(function* () {
35
35
  const response = yield* client.execute(input);
36
36
  yield* rates.observe(route, major, response.headers);
37
37
  if (isRateLimited(response.status)) {
@@ -45,20 +45,13 @@ export const initializeRest = Effect.gen(function* () {
45
45
  yield* checkStatus(response);
46
46
  const value = isEmpty(response.status) ? undefined : yield* readBoundedJson(response, 1024 * 1024, Schema.Unknown);
47
47
  return { retry: false, value };
48
- }),
49
- );
50
- if (!answer.retry) {
51
- return answer.value;
48
+ }).pipe(Effect.scoped);
49
+ if (!answer.retry) {
50
+ return answer.value;
51
+ }
52
52
  }
53
- }
54
- });
55
- return { lock, request };
56
- });
57
-
58
- export function restRequest(state: Effect.Success<typeof initializeRest>) {
59
- return (method: HttpMethod, path: string, body?: unknown) =>
60
- state.request(method, path, body).pipe(
61
- state.lock.withPermit,
62
- Effect.catchTag("HttpClientError", () => Effect.fail(new DiscordTransportError({}))),
63
- );
53
+ },
54
+ lock.withPermit,
55
+ Effect.catchTag("HttpClientError", () => Effect.fail(new DiscordTransportError({}))),
56
+ );
64
57
  }
@@ -0,0 +1,14 @@
1
+ import { HttpClient } from "effect/http";
2
+ import { defineService } from "@shivaedev/service/defineService.ts";
3
+ import { DiscordConfig } from "#DiscordConfig.ts";
4
+ import { DiscordToken } from "#DiscordToken.ts";
5
+ import { initialize } from "#rest/initialize.ts";
6
+ import { request } from "#rest/request.ts";
7
+ import { DiscordRestLimits } from "#rest-limits/service.ts";
8
+
9
+ export const DiscordRest = defineService({
10
+ id: "@shivaedev/discord/DiscordRest",
11
+ initialize,
12
+ methods: (state) => ({ request: request(state) }),
13
+ requires: [HttpClient.HttpClient, DiscordToken, DiscordConfig, DiscordRestLimits],
14
+ });
@@ -0,0 +1,9 @@
1
+ import { Effect, Ref } from "effect";
2
+
3
+ export const initialize = Effect.gen(function* () {
4
+ return {
5
+ blocked: new Map<string, number>(),
6
+ globalReset: yield* Ref.make(0),
7
+ routes: new Map<string, string>(),
8
+ };
9
+ });
@@ -0,0 +1,18 @@
1
+ import { Clock, Effect } from "effect";
2
+ import type { Headers } from "effect/http";
3
+ import type { initialize } from "#rest-limits/initialize.ts";
4
+
5
+ export function observe(state: Effect.Success<typeof initialize>) {
6
+ return Effect.fn("DiscordRestLimits.observe")(function* (route: string, major: string, headers: Headers.Headers) {
7
+ const bucket = headers["x-ratelimit-bucket"];
8
+ if (bucket !== undefined) {
9
+ state.routes.set(route, bucket);
10
+ }
11
+ if (headers["x-ratelimit-remaining"] === "0") {
12
+ state.blocked.set(
13
+ `${state.routes.get(route) ?? route}:${major}`,
14
+ (yield* Clock.currentTimeMillis) + Number(headers["x-ratelimit-reset-after"] ?? 0) * 1000,
15
+ );
16
+ }
17
+ });
18
+ }
@@ -0,0 +1,13 @@
1
+ import { Clock, Effect, Ref } from "effect";
2
+ import type { initialize } from "#rest-limits/initialize.ts";
3
+
4
+ export function refuse(state: Effect.Success<typeof initialize>) {
5
+ return Effect.fn("DiscordRestLimits.refuse")(function* (route: string, major: string, seconds: number, global: boolean) {
6
+ const reset = (yield* Clock.currentTimeMillis) + seconds * 1000;
7
+ if (global) {
8
+ yield* Ref.update(state.globalReset, (previous) => Math.max(previous, reset));
9
+ } else {
10
+ state.blocked.set(`${state.routes.get(route) ?? route}:${major}`, reset);
11
+ }
12
+ });
13
+ }
@@ -0,0 +1,12 @@
1
+ import { defineService } from "@shivaedev/service/defineService.ts";
2
+ import { initialize } from "#rest-limits/initialize.ts";
3
+ import { observe } from "#rest-limits/observe.ts";
4
+ import { refuse } from "#rest-limits/refuse.ts";
5
+ import { wait } from "#rest-limits/wait.ts";
6
+
7
+ export const DiscordRestLimits = defineService({
8
+ id: "@shivaedev/discord/DiscordRestLimits",
9
+ initialize,
10
+ methods: (state) => ({ observe: observe(state), refuse: refuse(state), wait: wait(state) }),
11
+ requires: [],
12
+ });
@@ -0,0 +1,12 @@
1
+ import { Clock, Effect, Ref } from "effect";
2
+ import type { initialize } from "#rest-limits/initialize.ts";
3
+
4
+ export function wait(state: Effect.Success<typeof initialize>) {
5
+ return Effect.fn("DiscordRestLimits.wait")(function* (route: string, major: string) {
6
+ const reset = Math.max(yield* Ref.get(state.globalReset), state.blocked.get(`${state.routes.get(route) ?? route}:${major}`) ?? 0);
7
+ const delay = reset - (yield* Clock.currentTimeMillis);
8
+ if (delay > 0) {
9
+ yield* Effect.sleep(delay);
10
+ }
11
+ });
12
+ }
@@ -1 +0,0 @@
1
- {"version":3,"file":"DiscordGateway.d.ts","sourceRoot":"","sources":["../src/DiscordGateway.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGhC,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAGxD,eAAO,MAAM,cAAc;;;;CAKzB,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"DiscordGateway.js","sourceRoot":"","sources":["../src/DiscordGateway.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE5C,MAAM,CAAC,MAAM,cAAc,GAAG,aAAa,CAAC;IAC3C,EAAE,EAAE,mCAAmC;IACvC,UAAU,EAAE,MAAM,CAAC,IAAI;IACvB,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC;IACpC,QAAQ,EAAE,CAAC,aAAa,EAAE,YAAY,EAAE,gBAAgB,CAAC;CACzD,CAAC,CAAC"}
@@ -1,8 +0,0 @@
1
- import { HttpClient } from "effect/http";
2
- import { DiscordToken } from "#DiscordToken.ts";
3
- export declare const DiscordRest: import("effect/Context").Service<"@shivaedev/discord/DiscordRest", Readonly<{
4
- readonly request: (method: import("effect/http/HttpMethod").HttpMethod, path: string, body?: unknown) => import("effect/Effect").Effect<unknown, import("./errors.ts").DiscordRateLimited | import("./errors.ts").DiscordTransportError | import("effect/http/HttpBody").HttpBodyError | import("@shivaedev/http/errors.ts").InvalidBodyLimit | import("@shivaedev/http/errors.ts").RemoteGone | import("@shivaedev/http/errors.ts").RemoteHttpError | import("@shivaedev/http/errors.ts").RemoteNotFound | import("@shivaedev/http/errors.ts").RemoteRateLimited | import("@shivaedev/http/errors.ts").RemoteUnauthorized | import("@shivaedev/http/errors.ts").ResponseBodyTooLarge | import("effect/Schema").SchemaError, never>;
5
- }>> & {
6
- readonly layer: import("effect/Layer").Layer<"@shivaedev/discord/DiscordRest", never, DiscordToken | HttpClient.HttpClient>;
7
- };
8
- //# sourceMappingURL=DiscordRest.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"DiscordRest.d.ts","sourceRoot":"","sources":["../src/DiscordRest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGzC,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAGhD,eAAO,MAAM,WAAW;;;;CAKtB,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"DiscordRest.js","sourceRoot":"","sources":["../src/DiscordRest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7D,MAAM,CAAC,MAAM,WAAW,GAAG,aAAa,CAAC;IACxC,EAAE,EAAE,gCAAgC;IACpC,UAAU,EAAE,cAAc;IAC1B,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;IACrD,QAAQ,EAAE,CAAC,UAAU,CAAC,UAAU,EAAE,YAAY,EAAE,aAAa,CAAC;CAC9D,CAAC,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"gatewayConnection.d.ts","sourceRoot":"","sources":["../src/gatewayConnection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,MAAM,EAAoB,MAAM,QAAQ,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAEvC,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAA2B,KAAK,eAAe,EAAqB,KAAK,cAAc,EAA2B,MAAM,qBAAqB,CAAC;AAErJ,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,8HAkFnH"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"gatewayConnection.js","sourceRoot":"","sources":["../src/gatewayConnection.ts"],"names":[],"mappings":"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"}
@@ -1,8 +0,0 @@
1
- import { Effect } from "effect";
2
- import type { Headers } from "effect/http";
3
- export declare function rateLimits(): {
4
- observe: (route: string, major: string, headers: Headers.Headers) => Effect.Effect<void, never, never>;
5
- refuse: (route: string, major: string, seconds: number, global: boolean) => Effect.Effect<void, never, never>;
6
- wait: (route: string, major: string) => Effect.Effect<void, never, never>;
7
- };
8
- //# sourceMappingURL=rateLimits.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"rateLimits.d.ts","sourceRoot":"","sources":["../src/rateLimits.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,MAAM,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,wBAAgB,UAAU;IAQxB,OAAO,UAAU,MAAM,SAAS,MAAM,WAAW,OAAO,CAAC,OAAO;IAUhE,MAAM,UAAU,MAAM,SAAS,MAAM,WAAW,MAAM,UAAU,OAAO;IASvE,IAAI,UAAU,MAAM,SAAS,MAAM;EAQpC"}
@@ -1,36 +0,0 @@
1
- import { Clock, Effect } from "effect";
2
- export function rateLimits() {
3
- const routes = new Map();
4
- const blocked = new Map();
5
- let globalReset = 0;
6
- function key(route, major) {
7
- return `${routes.get(route) ?? route}:${major}`;
8
- }
9
- return {
10
- observe: (route, major, headers) => Effect.gen(function* () {
11
- const bucket = headers["x-ratelimit-bucket"];
12
- if (bucket !== undefined) {
13
- routes.set(route, bucket);
14
- }
15
- if (headers["x-ratelimit-remaining"] === "0") {
16
- blocked.set(key(route, major), (yield* Clock.currentTimeMillis) + Number(headers["x-ratelimit-reset-after"] ?? 0) * 1000);
17
- }
18
- }),
19
- refuse: (route, major, seconds, global) => Effect.gen(function* () {
20
- const reset = (yield* Clock.currentTimeMillis) + seconds * 1000;
21
- if (global) {
22
- globalReset = Math.max(globalReset, reset);
23
- }
24
- else {
25
- blocked.set(key(route, major), reset);
26
- }
27
- }),
28
- wait: (route, major) => Effect.gen(function* () {
29
- const delay = Math.max(globalReset, blocked.get(key(route, major)) ?? 0) - (yield* Clock.currentTimeMillis);
30
- if (delay > 0) {
31
- yield* Effect.sleep(delay);
32
- }
33
- }),
34
- };
35
- }
36
- //# sourceMappingURL=rateLimits.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"rateLimits.js","sourceRoot":"","sources":["../src/rateLimits.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGvC,MAAM,UAAU,UAAU;IACzB,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC1C,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,SAAS,GAAG,CAAC,KAAa,EAAE,KAAa;QACxC,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,KAAK,EAAE,CAAC;IACjD,CAAC;IACD,OAAO;QACN,OAAO,EAAE,CAAC,KAAa,EAAE,KAAa,EAAE,OAAwB,EAAE,EAAE,CACnE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YACnB,MAAM,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;YAC7C,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBAC1B,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;YAC3B,CAAC;YACD,IAAI,OAAO,CAAC,uBAAuB,CAAC,KAAK,GAAG,EAAE,CAAC;gBAC9C,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,yBAAyB,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;YAC3H,CAAC;QACF,CAAC,CAAC;QACH,MAAM,EAAE,CAAC,KAAa,EAAE,KAAa,EAAE,OAAe,EAAE,MAAe,EAAE,EAAE,CAC1E,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YACnB,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,GAAG,OAAO,GAAG,IAAI,CAAC;YAChE,IAAI,MAAM,EAAE,CAAC;gBACZ,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;YAC5C,CAAC;iBAAM,CAAC;gBACP,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC;YACvC,CAAC;QACF,CAAC,CAAC;QACH,IAAI,EAAE,CAAC,KAAa,EAAE,KAAa,EAAE,EAAE,CACtC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YACnB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;YAC5G,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;gBACf,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAC5B,CAAC;QACF,CAAC,CAAC;KACH,CAAC;AACH,CAAC"}
@@ -1,11 +0,0 @@
1
- import { Effect, Schema, Semaphore } from "effect";
2
- import { HttpClient } from "effect/http";
3
- import type { HttpMethod } from "effect/http/HttpMethod";
4
- import { DiscordToken } from "#DiscordToken.ts";
5
- import { DiscordRateLimited, DiscordTransportError } from "#errors.ts";
6
- export declare const initializeRest: Effect.Effect<{
7
- lock: Semaphore.Semaphore;
8
- request: (method: HttpMethod, path: string, body?: unknown) => Effect.Effect<unknown, DiscordRateLimited | import("effect/http/HttpBody").HttpBodyError | import("effect/http/HttpClientError").HttpClientError | import("@shivaedev/http/errors.ts").InvalidBodyLimit | import("@shivaedev/http/errors.ts").RemoteGone | import("@shivaedev/http/errors.ts").RemoteHttpError | import("@shivaedev/http/errors.ts").RemoteNotFound | import("@shivaedev/http/errors.ts").RemoteRateLimited | import("@shivaedev/http/errors.ts").RemoteUnauthorized | import("@shivaedev/http/errors.ts").ResponseBodyTooLarge | Schema.SchemaError, never>;
9
- }, never, DiscordToken | HttpClient.HttpClient>;
10
- export declare function restRequest(state: Effect.Success<typeof initializeRest>): (method: HttpMethod, path: string, body?: unknown) => Effect.Effect<unknown, DiscordRateLimited | DiscordTransportError | import("effect/http/HttpBody").HttpBodyError | import("@shivaedev/http/errors.ts").InvalidBodyLimit | import("@shivaedev/http/errors.ts").RemoteGone | import("@shivaedev/http/errors.ts").RemoteHttpError | import("@shivaedev/http/errors.ts").RemoteNotFound | import("@shivaedev/http/errors.ts").RemoteRateLimited | import("@shivaedev/http/errors.ts").RemoteUnauthorized | import("@shivaedev/http/errors.ts").ResponseBodyTooLarge | Schema.SchemaError, never>;
11
- //# sourceMappingURL=restClient.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"restClient.d.ts","sourceRoot":"","sources":["../src/restClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAY,MAAM,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAqB,MAAM,aAAa,CAAC;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAMzD,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAOvE,eAAO,MAAM,cAAc;;;+CAuCzB,CAAC;AAEH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO,cAAc,CAAC,YACvD,UAAU,QAAQ,MAAM,SAAS,OAAO,khBAKxD"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"restClient.js","sourceRoot":"","sources":["../src/restClient.ts"],"names":[],"mappings":"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"}
@@ -1,6 +0,0 @@
1
- import { Effect } from "effect";
2
- import { DiscordGatewayClosed } from "#errors.ts";
3
- import { GatewayTransport } from "#GatewayTransport.ts";
4
- import type { GatewayDispatch } from "#gatewayProtocol.ts";
5
- export declare const runGateway: (dispatch: (event: GatewayDispatch) => Effect.Effect<void>) => Effect.Effect<never, DiscordGatewayClosed | import("#errors.ts").DiscordGatewayProtocolError, import("./DiscordToken.ts").DiscordToken | GatewayTransport>;
6
- //# sourceMappingURL=runGateway.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"runGateway.d.ts","sourceRoot":"","sources":["../src/runGateway.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGhC,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExD,OAAO,KAAK,EAAE,eAAe,EAAkB,MAAM,qBAAqB,CAAC;AAE3E,eAAO,MAAM,UAAU,qBAAkD,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,+JAuB9G,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"runGateway.js","sourceRoot":"","sources":["../src/runGateway.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAG1D,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,QAAyD;IAC/G,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,aAAa,CAAC;IACpC,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,gBAAgB,CAAC;IAC1C,MAAM,OAAO,GAAmB,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;IAC/F,OAAO,IAAI,EAAE,CAAC;QACb,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,SAAS,IAAI,MAAM,CAAC,UAAU,sBAAsB,CAAC,CAAC;QACtG,KAAK,CAAC,CAAC,iBAAiB,CAAC,OAAO,EAAE,QAAQ,CAAC;aACzC,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;aAClD,IAAI,CACJ,MAAM,CAAC,QAAQ,CAAC,aAAa,EAAE,CAAC,KAAK,EAAE,EAAE;YACxC,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,KAAK,kBAAkB,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;YACtF,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,EAAE,CAAC;gBAC3E,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,oBAAoB,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YACxD,CAAC;YACD,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;gBACpC,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;gBAC9B,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;gBAC9B,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;YACzB,CAAC;YACD,OAAO,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QACjC,CAAC,CAAC,CACF,CAAC;IACJ,CAAC;AACF,CAAC,CAAC,CAAC"}
@@ -1,12 +0,0 @@
1
- import { HttpClient } from "effect/http";
2
- import { defineService } from "@shivaedev/service/defineService.ts";
3
- import { DiscordConfig } from "#DiscordConfig.ts";
4
- import { DiscordToken } from "#DiscordToken.ts";
5
- import { initializeRest, restRequest } from "#restClient.ts";
6
-
7
- export const DiscordRest = defineService({
8
- id: "@shivaedev/discord/DiscordRest",
9
- initialize: initializeRest,
10
- methods: (state) => ({ request: restRequest(state) }),
11
- requires: [HttpClient.HttpClient, DiscordToken, DiscordConfig],
12
- });
package/src/rateLimits.ts DELETED
@@ -1,39 +0,0 @@
1
- import { Clock, Effect } from "effect";
2
- import type { Headers } from "effect/http";
3
-
4
- export function rateLimits() {
5
- const routes = new Map<string, string>();
6
- const blocked = new Map<string, number>();
7
- let globalReset = 0;
8
- function key(route: string, major: string) {
9
- return `${routes.get(route) ?? route}:${major}`;
10
- }
11
- return {
12
- observe: (route: string, major: string, headers: Headers.Headers) =>
13
- Effect.gen(function* () {
14
- const bucket = headers["x-ratelimit-bucket"];
15
- if (bucket !== undefined) {
16
- routes.set(route, bucket);
17
- }
18
- if (headers["x-ratelimit-remaining"] === "0") {
19
- blocked.set(key(route, major), (yield* Clock.currentTimeMillis) + Number(headers["x-ratelimit-reset-after"] ?? 0) * 1000);
20
- }
21
- }),
22
- refuse: (route: string, major: string, seconds: number, global: boolean) =>
23
- Effect.gen(function* () {
24
- const reset = (yield* Clock.currentTimeMillis) + seconds * 1000;
25
- if (global) {
26
- globalReset = Math.max(globalReset, reset);
27
- } else {
28
- blocked.set(key(route, major), reset);
29
- }
30
- }),
31
- wait: (route: string, major: string) =>
32
- Effect.gen(function* () {
33
- const delay = Math.max(globalReset, blocked.get(key(route, major)) ?? 0) - (yield* Clock.currentTimeMillis);
34
- if (delay > 0) {
35
- yield* Effect.sleep(delay);
36
- }
37
- }),
38
- };
39
- }