dfx 0.42.2 → 0.42.4

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 (97) hide show
  1. package/Cache/driver.js.map +1 -1
  2. package/Cache/memory.js.map +1 -1
  3. package/Cache/memoryTTL.js.map +1 -1
  4. package/Cache/prelude.js.map +1 -1
  5. package/Cache.js.map +1 -1
  6. package/DiscordConfig.js.map +1 -1
  7. package/DiscordGateway/DiscordWS.js.map +1 -1
  8. package/DiscordGateway/Shard/heartbeats.js.map +1 -1
  9. package/DiscordGateway/Shard/identify.js.map +1 -1
  10. package/DiscordGateway/Shard/invalidSession.js.map +1 -1
  11. package/DiscordGateway/Shard/sendEvents.js.map +1 -1
  12. package/DiscordGateway/Shard/utils.js.map +1 -1
  13. package/DiscordGateway/Shard.js.map +1 -1
  14. package/DiscordGateway/ShardStore.js.map +1 -1
  15. package/DiscordGateway/Sharder.js.map +1 -1
  16. package/DiscordGateway/WS.js +11 -14
  17. package/DiscordGateway/WS.js.map +1 -1
  18. package/DiscordGateway.js.map +1 -1
  19. package/DiscordREST/types.js.map +1 -1
  20. package/DiscordREST/utils.js.map +1 -1
  21. package/DiscordREST.js.map +1 -1
  22. package/Helpers/flags.js.map +1 -1
  23. package/Helpers/intents.js.map +1 -1
  24. package/Helpers/interactions.js.map +1 -1
  25. package/Helpers/members.js.map +1 -1
  26. package/Helpers/permissions.js.map +1 -1
  27. package/Helpers/ui.js.map +1 -1
  28. package/Interactions/context.js.map +1 -1
  29. package/Interactions/definitions.js.map +1 -1
  30. package/Interactions/gateway.js.map +1 -1
  31. package/Interactions/handlers.js.map +1 -1
  32. package/Interactions/index.js.map +1 -1
  33. package/Interactions/utils.js.map +1 -1
  34. package/Interactions/webhook.js.map +1 -1
  35. package/Log.js.map +1 -1
  36. package/RateLimit/memory.js.map +1 -1
  37. package/RateLimit/utils.js.map +1 -1
  38. package/RateLimit.js.map +1 -1
  39. package/_common.js.map +1 -1
  40. package/gateway.js.map +1 -1
  41. package/global.js.map +1 -1
  42. package/index.js.map +1 -1
  43. package/package.json +50 -52
  44. package/src/Cache/driver.ts +31 -0
  45. package/src/Cache/memory.ts +76 -0
  46. package/src/Cache/memoryTTL.ts +201 -0
  47. package/src/Cache/prelude.ts +215 -0
  48. package/src/Cache.ts +140 -0
  49. package/src/DiscordConfig.ts +48 -0
  50. package/src/DiscordGateway/DiscordWS.ts +74 -0
  51. package/src/DiscordGateway/Shard/heartbeats.ts +42 -0
  52. package/src/DiscordGateway/Shard/identify.ts +52 -0
  53. package/src/DiscordGateway/Shard/invalidSession.ts +10 -0
  54. package/src/DiscordGateway/Shard/sendEvents.ts +37 -0
  55. package/src/DiscordGateway/Shard/utils.ts +14 -0
  56. package/src/DiscordGateway/Shard.ts +152 -0
  57. package/src/DiscordGateway/ShardStore.ts +33 -0
  58. package/src/DiscordGateway/Sharder.ts +102 -0
  59. package/src/DiscordGateway/WS.ts +118 -0
  60. package/src/DiscordGateway.ts +43 -0
  61. package/src/DiscordREST/types.ts +13 -0
  62. package/src/DiscordREST/utils.ts +33 -0
  63. package/src/DiscordREST.ts +203 -0
  64. package/src/Helpers/flags.ts +68 -0
  65. package/src/Helpers/intents.ts +34 -0
  66. package/src/Helpers/interactions.ts +229 -0
  67. package/src/Helpers/members.ts +14 -0
  68. package/src/Helpers/permissions.ts +140 -0
  69. package/src/Helpers/ui.ts +103 -0
  70. package/src/Interactions/context.ts +132 -0
  71. package/src/Interactions/definitions.ts +309 -0
  72. package/src/Interactions/gateway.ts +71 -0
  73. package/src/Interactions/handlers.ts +130 -0
  74. package/src/Interactions/index.ts +108 -0
  75. package/src/Interactions/utils.ts +81 -0
  76. package/src/Interactions/webhook.ts +110 -0
  77. package/src/Log.ts +17 -0
  78. package/src/RateLimit/memory.ts +57 -0
  79. package/src/RateLimit/utils.ts +27 -0
  80. package/src/RateLimit.ts +69 -0
  81. package/src/_common.ts +43 -0
  82. package/src/gateway.ts +38 -0
  83. package/src/global.ts +45 -0
  84. package/src/index.ts +20 -0
  85. package/src/types.ts +6368 -0
  86. package/src/utils/effect.ts +0 -0
  87. package/src/utils/hub.ts +47 -0
  88. package/src/utils/json.d.ts +1 -0
  89. package/src/utils/tsplus.ts +10 -0
  90. package/src/webhooks.ts +41 -0
  91. package/tsconfig.json +23 -0
  92. package/tsplus.config.json +8 -0
  93. package/types.js.map +1 -1
  94. package/utils/effect.js.map +1 -1
  95. package/utils/hub.js.map +1 -1
  96. package/utils/tsplus.js.map +1 -1
  97. package/webhooks.js.map +1 -1
@@ -0,0 +1,110 @@
1
+ import Nacl from "tweetnacl"
2
+ import * as D from "./definitions.js"
3
+ import { DefinitionNotFound, handlers } from "./handlers.js"
4
+ import { InteractionBuilder, Interaction } from "./index.js"
5
+ import { fromHex } from "./utils.js"
6
+
7
+ export class BadWebhookSignature {
8
+ readonly _tag = "BadWebhookSignature"
9
+ }
10
+
11
+ export type Headers = Record<string, string | string[] | undefined>
12
+
13
+ const checkSignature = (
14
+ publicKey: Uint8Array,
15
+ headers: Headers,
16
+ body: string,
17
+ ) =>
18
+ Maybe.struct({
19
+ signature: Maybe.fromNullable(headers["x-signature-ed25519"]),
20
+ timestamp: Maybe.fromNullable(headers["x-signature-timestamp"]),
21
+ })
22
+ .filter(a => {
23
+ const enc = new TextEncoder()
24
+ return Nacl.sign.detached.verify(
25
+ enc.encode(a.timestamp + body),
26
+ fromHex(`${a.signature}`),
27
+ publicKey,
28
+ )
29
+ })
30
+ .toEither(() => new BadWebhookSignature()).asUnit
31
+
32
+ export interface MakeConfigOpts {
33
+ applicationId: string
34
+ publicKey: ConfigSecret
35
+ }
36
+ const makeConfig = ({ applicationId, publicKey }: MakeConfigOpts) => ({
37
+ applicationId,
38
+ publicKey: fromHex(publicKey.value),
39
+ })
40
+ export interface WebhookConfig extends ReturnType<typeof makeConfig> {}
41
+ export const WebhookConfig = Tag<WebhookConfig>()
42
+ export const makeConfigLayer = flow(makeConfig, _ =>
43
+ Layer.succeed(WebhookConfig, _),
44
+ )
45
+ export const makeFromConfig = (a: Config<MakeConfigOpts>) =>
46
+ a.config.map(makeConfig).toLayer(WebhookConfig)
47
+
48
+ export class WebhookParseError {
49
+ readonly _tag = "WebhookParseError"
50
+ constructor(readonly reason: unknown) {}
51
+ }
52
+
53
+ const fromHeadersAndBody = (headers: Headers, body: string) =>
54
+ Do($ => {
55
+ const { publicKey } = $(WebhookConfig)
56
+ $(checkSignature(publicKey, headers, body))
57
+ return $(
58
+ Effect.tryCatch(
59
+ () => JSON.parse(body) as Discord.Interaction,
60
+ reason => new WebhookParseError(reason),
61
+ ),
62
+ )
63
+ })
64
+
65
+ const run = <R, E>(definitions: D.InteractionDefinition<R, E>[]) => {
66
+ const handler = handlers(definitions)
67
+ return (headers: Headers, body: string) =>
68
+ Do($ => {
69
+ const interaction = $(fromHeadersAndBody(headers, body))
70
+ return $(
71
+ handler[interaction.type](interaction).provideService(
72
+ Interaction,
73
+ interaction,
74
+ ),
75
+ )
76
+ })
77
+ }
78
+
79
+ export interface HandleWebhookOpts<E> {
80
+ headers: Headers
81
+ body: string
82
+ success: (a: Discord.InteractionResponse) => Effect<never, never, void>
83
+ error: (e: Cause<E>) => Effect<never, never, void>
84
+ }
85
+
86
+ /**
87
+ * @tsplus getter dfx/InteractionBuilder webhookHandler
88
+ */
89
+ export const makeHandler = <R, E>(ix: InteractionBuilder<R, E>) => {
90
+ const handle = run(ix.definitions)
91
+
92
+ return ({
93
+ headers,
94
+ body,
95
+ success,
96
+ error,
97
+ }: HandleWebhookOpts<
98
+ E | WebhookParseError | BadWebhookSignature | DefinitionNotFound
99
+ >) => handle(headers, body).flatMap(success).catchAllCause(error)
100
+ }
101
+
102
+ /**
103
+ * @tsplus getter dfx/InteractionBuilder simpleWebhookHandler
104
+ */
105
+ export const makeSimpleHandler = <R, E>(ix: InteractionBuilder<R, E>) => {
106
+ const handle = run(ix.definitions)
107
+
108
+ return ({ headers, body }: { headers: Headers; body: string }) =>
109
+ handle(headers, body)
110
+ }
package/src/Log.ts ADDED
@@ -0,0 +1,17 @@
1
+ const make = (debug = false) => ({
2
+ info: (...args: any[]) =>
3
+ Effect.sync(() => {
4
+ console.error("INFO", ...args)
5
+ }),
6
+ debug: (...args: any[]) =>
7
+ debug
8
+ ? Effect.sync(() => {
9
+ console.error("DEBUG", ...args)
10
+ })
11
+ : Effect.unit(),
12
+ })
13
+
14
+ export interface Log extends ReturnType<typeof make> {}
15
+ export const Log = Tag<Log>()
16
+ export const LiveLog = Layer.succeed(Log, make(false))
17
+ export const LiveLogDebug = Layer.succeed(Log, make(true))
@@ -0,0 +1,57 @@
1
+ import { BucketDetails, RateLimitStore } from "../RateLimit.js"
2
+
3
+ interface Counter {
4
+ count: number
5
+ expires: number
6
+ }
7
+
8
+ export const make = (): RateLimitStore => {
9
+ const buckets = new Map<string, BucketDetails>()
10
+ const routes = new Map<string, string>()
11
+ const counters = new Map<string, Counter>()
12
+
13
+ const getCounter = (key: string) =>
14
+ Maybe.fromNullable(counters.get(key)).filter(c => c.expires > Date.now())
15
+
16
+ const getBucketForRoute = (route: string) =>
17
+ Effect.sync(() => Maybe.fromNullable(buckets.get(routes.get(route)!)))
18
+
19
+ return {
20
+ hasBucket: key => Effect.sync(() => buckets.has(key)),
21
+
22
+ putBucket: bucket =>
23
+ Effect.sync(() => {
24
+ buckets.set(bucket.key, bucket)
25
+ }),
26
+
27
+ getBucketForRoute,
28
+
29
+ putBucketRoute: (route, bucket) =>
30
+ Effect.sync(() => {
31
+ routes.set(route, bucket)
32
+ }),
33
+
34
+ removeCounter: key =>
35
+ Effect.sync(() => {
36
+ counters.delete(key)
37
+ }),
38
+
39
+ incrementCounter: (key, window, limit) =>
40
+ Effect.sync(() => {
41
+ const now = Date.now()
42
+ const perRequest = Math.ceil(window / limit)
43
+ const counter = getCounter(key).getOrElse(
44
+ (): Counter => ({
45
+ expires: now,
46
+ count: 0,
47
+ }),
48
+ )
49
+
50
+ const count = counter.count + 1
51
+ const expires = counter.expires + perRequest
52
+ counters.set(key, { ...counter, count, expires })
53
+
54
+ return [count, expires - now]
55
+ }),
56
+ }
57
+ }
@@ -0,0 +1,27 @@
1
+ import { millis, zero } from "@effect/data/Duration"
2
+
3
+ export const delayFrom = (
4
+ window: number,
5
+ limit: number,
6
+ count: number,
7
+ ttl: number,
8
+ ): Duration => {
9
+ const perRequest = Math.ceil(window / limit)
10
+
11
+ const totalTime = count * perRequest
12
+ const elapsedTime = totalTime - ttl
13
+ const elapsedWindows = Math.floor(elapsedTime / window)
14
+ const completedRequests = elapsedWindows * limit
15
+ const remainingRequests = count - completedRequests
16
+ const remainingWindows = Math.floor((remainingRequests - 1) / limit)
17
+ const delayRemainder = elapsedTime % window
18
+
19
+ const requestRemainder = count % limit
20
+ const staggerDelay = requestRemainder * 50
21
+
22
+ if (remainingWindows === 0) {
23
+ return zero
24
+ }
25
+
26
+ return millis(remainingWindows * window - delayRemainder + staggerDelay)
27
+ }
@@ -0,0 +1,69 @@
1
+ import { delayFrom } from "./RateLimit/utils.js"
2
+ import * as Memory from "./RateLimit/memory.js"
3
+ import { Log } from "dfx/Log"
4
+
5
+ export type BucketDetails = {
6
+ key: "global" | string
7
+ resetAfter: number
8
+ limit: number
9
+ }
10
+
11
+ export interface RateLimitStore {
12
+ hasBucket: (bucketKey: string) => Effect<never, never, boolean>
13
+
14
+ putBucket: (bucket: BucketDetails) => Effect<never, never, void>
15
+
16
+ getBucketForRoute: (
17
+ route: string,
18
+ ) => Effect<never, never, Maybe<BucketDetails>>
19
+
20
+ putBucketRoute: (
21
+ route: string,
22
+ bucketKey: string,
23
+ ) => Effect<never, never, void>
24
+
25
+ incrementCounter: (
26
+ key: string,
27
+ window: number,
28
+ limit: number,
29
+ ) => Effect<never, never, readonly [count: number, ttl: number]>
30
+
31
+ removeCounter: (key: string) => Effect<never, never, void>
32
+ }
33
+
34
+ export const RateLimitStore = Tag<RateLimitStore>()
35
+ export const LiveMemoryRateLimitStore = Layer.sync(RateLimitStore, Memory.make)
36
+
37
+ const makeLimiter = Do($ => {
38
+ const store = $(RateLimitStore)
39
+ const log = $(Log)
40
+
41
+ const maybeWait = (
42
+ key: string,
43
+ window: Duration,
44
+ limit: number,
45
+ multiplier = 1.05,
46
+ ) => {
47
+ const windowMs = window.millis * multiplier
48
+
49
+ return store
50
+ .incrementCounter(key, windowMs, limit)
51
+ .map(([count, ttl]) => delayFrom(windowMs, limit, count, ttl))
52
+ .tap(d =>
53
+ log.debug("RateLimitStore maybeWait", {
54
+ key,
55
+ window: window.millis,
56
+ windowMs,
57
+ limit,
58
+ delay: d.millis,
59
+ }),
60
+ )
61
+ .tap(_ => (_.millis === 0 ? Effect.unit() : Effect.sleep(_))).asUnit
62
+ }
63
+
64
+ return { maybeWait }
65
+ })
66
+
67
+ export interface RateLimiter extends Effect.Success<typeof makeLimiter> {}
68
+ export const RateLimiter = Tag<RateLimiter>()
69
+ export const LiveRateLimiter = Layer.effect(RateLimiter, makeLimiter)
package/src/_common.ts ADDED
@@ -0,0 +1,43 @@
1
+ import type { Schedule as _Schedule } from "@effect/io/Schedule"
2
+ import * as Deferred from "@effect/io/Deferred"
3
+
4
+ export type { Cause } from "@effect/io/Cause"
5
+ export type { Config } from "@effect/io/Config"
6
+ export type { ConfigSecret } from "@effect/io/Config/Secret"
7
+ export type { ConfigError } from "@effect/io/Config/Error"
8
+ export type { Deferred } from "@effect/io/Deferred"
9
+ export type { Effect } from "@effect/io/Effect"
10
+ export type { Exit } from "@effect/io/Exit"
11
+ export type { RuntimeFiber } from "@effect/io/Fiber"
12
+ export type { Hub } from "@effect/io/Hub"
13
+ export type { Layer } from "@effect/io/Layer"
14
+ export type { Dequeue, Enqueue, Queue } from "@effect/io/Queue"
15
+ export type { Ref } from "@effect/io/Ref"
16
+ export type { Scope } from "@effect/io/Scope"
17
+
18
+ export type { Stream } from "@effect/stream/Stream"
19
+
20
+ export type { Chunk } from "@effect/data/Chunk"
21
+ export { Context, Tag } from "@effect/data/Context"
22
+ export type { Duration } from "@effect/data/Duration"
23
+ export type { Equal } from "@effect/data/Equal"
24
+ export type { HashMap } from "@effect/data/HashMap"
25
+ export type { HashSet } from "@effect/data/HashSet"
26
+
27
+ export type { Option as Maybe } from "@effect/data/Option"
28
+ export type { Either } from "@effect/data/Either"
29
+
30
+ export * as Discord from "./types.js"
31
+
32
+ /**
33
+ * @tsplus type effect/io/Schedule
34
+ * @tsplus companion effect/io/Schedule.Ops
35
+ */
36
+ export type Schedule<Env, In, Out> = _Schedule<Env, In, Out>
37
+
38
+ /**
39
+ * @tsplus getter effect/io/Deferred await
40
+ */
41
+ export const deferredAwait: <E, A>(
42
+ self: Deferred.Deferred<E, A>,
43
+ ) => Effect<never, E, A> = Deferred.await
package/src/gateway.ts ADDED
@@ -0,0 +1,38 @@
1
+ import { LiveFetchRequestExecutor } from "@effect-http/client"
2
+ import { DiscordConfig, LiveDiscordREST, Log } from "dfx"
3
+ import { LiveDiscordGateway } from "./DiscordGateway.js"
4
+ import { LiveJsonDiscordWSCodec } from "./DiscordGateway/DiscordWS.js"
5
+ import { LiveMemoryShardStore } from "./DiscordGateway/ShardStore.js"
6
+ import { LiveMemoryRateLimitStore, LiveRateLimiter } from "./RateLimit.js"
7
+
8
+ export * as CachePrelude from "./Cache/prelude.js"
9
+ export { DiscordGateway, LiveDiscordGateway } from "./DiscordGateway.js"
10
+ export * as DiscordWS from "./DiscordGateway/DiscordWS.js"
11
+ export * as Shard from "./DiscordGateway/Shard.js"
12
+ export * as ShardStore from "./DiscordGateway/ShardStore.js"
13
+ export * as WS from "./DiscordGateway/WS.js"
14
+ export { run as runIx } from "./Interactions/gateway.js"
15
+
16
+ export const MemoryRateLimit = LiveMemoryRateLimitStore >> LiveRateLimiter
17
+
18
+ export const MemoryBot =
19
+ (LiveMemoryShardStore + LiveMemoryRateLimitStore + LiveJsonDiscordWSCodec) >>
20
+ ((LiveDiscordREST > LiveDiscordGateway) + MemoryRateLimit)
21
+
22
+ export const makeLiveWithoutFetch = (
23
+ config: Config.Wrap<DiscordConfig.MakeOpts>,
24
+ debug = false,
25
+ ) => {
26
+ const LiveLog = debug ? Log.LiveLogDebug : Log.LiveLog
27
+ const LiveConfig = DiscordConfig.makeFromConfig(Config.unwrap(config))
28
+ const LiveEnv = LiveLog + LiveConfig > MemoryBot
29
+
30
+ return LiveEnv
31
+ }
32
+
33
+ export const makeLive = (
34
+ config: Config.Wrap<DiscordConfig.MakeOpts>,
35
+ debug = false,
36
+ ) => {
37
+ return LiveFetchRequestExecutor >> makeLiveWithoutFetch(config, debug)
38
+ }
package/src/global.ts ADDED
@@ -0,0 +1,45 @@
1
+ /**
2
+ * @tsplus global
3
+ */
4
+ import type {
5
+ Cause,
6
+ Config,
7
+ ConfigSecret,
8
+ ConfigError,
9
+ Deferred,
10
+ Effect,
11
+ Exit,
12
+ Hub,
13
+ Layer,
14
+ RuntimeFiber,
15
+ Dequeue,
16
+ Enqueue,
17
+ Queue,
18
+ Schedule,
19
+ Scope,
20
+ Stream,
21
+ Chunk,
22
+ Context,
23
+ Duration,
24
+ Equal,
25
+ Either,
26
+ HashMap,
27
+ Maybe,
28
+ Ref,
29
+ HashSet,
30
+ } from "dfx/_common"
31
+
32
+ /**
33
+ * @tsplus global
34
+ */
35
+ import { Tag, Discord } from "dfx/_common"
36
+
37
+ /**
38
+ * @tsplus global
39
+ */
40
+ import { pipe, flow, identity } from "@effect/data/Function"
41
+
42
+ /**
43
+ * @tsplus global
44
+ */
45
+ import type { LazyArg } from "@effect/data/Function"
package/src/index.ts ADDED
@@ -0,0 +1,20 @@
1
+ export * as Discord from "./types.js"
2
+ export * as DiscordConfig from "./DiscordConfig.js"
3
+ export { DiscordREST, LiveDiscordREST } from "./DiscordREST.js"
4
+ export * as Ix from "./Interactions/index.js"
5
+ export * as Log from "./Log.js"
6
+ export {
7
+ BucketDetails,
8
+ RateLimitStore,
9
+ LiveMemoryRateLimitStore,
10
+ RateLimiter,
11
+ LiveRateLimiter,
12
+ } from "./RateLimit.js"
13
+ export * as Cache from "./Cache.js"
14
+
15
+ export * as Flags from "./Helpers/flags.js"
16
+ export * as Intents from "./Helpers/intents.js"
17
+ export * as IxHelpers from "./Helpers/interactions.js"
18
+ export * as Members from "./Helpers/members.js"
19
+ export * as Perms from "./Helpers/permissions.js"
20
+ export * as UI from "./Helpers/ui.js"