@atproto/bsky 0.0.215 → 0.0.217

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 (105) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/api/app/bsky/feed/searchPosts.d.ts.map +1 -1
  3. package/dist/api/app/bsky/feed/searchPosts.js +6 -4
  4. package/dist/api/app/bsky/feed/searchPosts.js.map +1 -1
  5. package/dist/api/app/bsky/graph/getSuggestedFollowsByActor.js +2 -0
  6. package/dist/api/app/bsky/graph/getSuggestedFollowsByActor.js.map +1 -1
  7. package/dist/api/app/bsky/unspecced/getPostThreadV2.js +1 -1
  8. package/dist/api/app/bsky/unspecced/getPostThreadV2.js.map +1 -1
  9. package/dist/api/app/bsky/unspecced/getSuggestedOnboardingUsers.d.ts.map +1 -1
  10. package/dist/api/app/bsky/unspecced/getSuggestedOnboardingUsers.js +10 -3
  11. package/dist/api/app/bsky/unspecced/getSuggestedOnboardingUsers.js.map +1 -1
  12. package/dist/api/app/bsky/unspecced/getSuggestedUsers.d.ts.map +1 -1
  13. package/dist/api/app/bsky/unspecced/getSuggestedUsers.js +9 -2
  14. package/dist/api/app/bsky/unspecced/getSuggestedUsers.js.map +1 -1
  15. package/dist/config.d.ts +2 -0
  16. package/dist/config.d.ts.map +1 -1
  17. package/dist/config.js +5 -0
  18. package/dist/config.js.map +1 -1
  19. package/dist/context.d.ts +3 -3
  20. package/dist/context.d.ts.map +1 -1
  21. package/dist/context.js +2 -2
  22. package/dist/context.js.map +1 -1
  23. package/dist/feature-gates/gates.d.ts +5 -0
  24. package/dist/feature-gates/gates.d.ts.map +1 -0
  25. package/dist/feature-gates/gates.js +6 -0
  26. package/dist/feature-gates/gates.js.map +1 -0
  27. package/dist/feature-gates/index.d.ts +24 -0
  28. package/dist/feature-gates/index.d.ts.map +1 -0
  29. package/dist/feature-gates/index.js +135 -0
  30. package/dist/feature-gates/index.js.map +1 -0
  31. package/dist/feature-gates/metrics.d.ts +32 -0
  32. package/dist/feature-gates/metrics.d.ts.map +1 -0
  33. package/dist/feature-gates/metrics.js +100 -0
  34. package/dist/feature-gates/metrics.js.map +1 -0
  35. package/dist/feature-gates/metrics.test.d.ts +2 -0
  36. package/dist/feature-gates/metrics.test.d.ts.map +1 -0
  37. package/dist/feature-gates/metrics.test.js +152 -0
  38. package/dist/feature-gates/metrics.test.js.map +1 -0
  39. package/dist/feature-gates/types.d.ts +49 -0
  40. package/dist/feature-gates/types.d.ts.map +1 -0
  41. package/dist/feature-gates/types.js +3 -0
  42. package/dist/feature-gates/types.js.map +1 -0
  43. package/dist/feature-gates/utils.d.ts +21 -0
  44. package/dist/feature-gates/utils.d.ts.map +1 -0
  45. package/dist/feature-gates/utils.js +85 -0
  46. package/dist/feature-gates/utils.js.map +1 -0
  47. package/dist/hydration/hydrator.d.ts +8 -3
  48. package/dist/hydration/hydrator.d.ts.map +1 -1
  49. package/dist/hydration/hydrator.js +9 -5
  50. package/dist/hydration/hydrator.js.map +1 -1
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/index.js +7 -6
  53. package/dist/index.js.map +1 -1
  54. package/dist/lexicon/index.d.ts +2 -2
  55. package/dist/lexicon/index.d.ts.map +1 -1
  56. package/dist/lexicon/index.js +4 -4
  57. package/dist/lexicon/index.js.map +1 -1
  58. package/dist/lexicon/lexicons.d.ts +116 -100
  59. package/dist/lexicon/lexicons.d.ts.map +1 -1
  60. package/dist/lexicon/lexicons.js +59 -51
  61. package/dist/lexicon/lexicons.js.map +1 -1
  62. package/dist/lexicon/types/app/bsky/graph/getSuggestedFollowsByActor.d.ts +3 -1
  63. package/dist/lexicon/types/app/bsky/graph/getSuggestedFollowsByActor.d.ts.map +1 -1
  64. package/dist/lexicon/types/app/bsky/graph/getSuggestedFollowsByActor.js.map +1 -1
  65. package/dist/lexicon/types/app/bsky/unspecced/{getSuggestedOnboardingUsersSkeleton.d.ts → getOnboardingSuggestedUsersSkeleton.d.ts} +1 -1
  66. package/dist/lexicon/types/app/bsky/unspecced/{getSuggestedOnboardingUsersSkeleton.d.ts.map → getOnboardingSuggestedUsersSkeleton.d.ts.map} +1 -1
  67. package/dist/lexicon/types/app/bsky/unspecced/{getSuggestedOnboardingUsersSkeleton.js → getOnboardingSuggestedUsersSkeleton.js} +2 -2
  68. package/dist/lexicon/types/app/bsky/unspecced/{getSuggestedOnboardingUsersSkeleton.js.map → getOnboardingSuggestedUsersSkeleton.js.map} +1 -1
  69. package/dist/lexicon/types/app/bsky/unspecced/getSuggestionsSkeleton.d.ts +3 -1
  70. package/dist/lexicon/types/app/bsky/unspecced/getSuggestionsSkeleton.d.ts.map +1 -1
  71. package/dist/lexicon/types/app/bsky/unspecced/getSuggestionsSkeleton.js.map +1 -1
  72. package/dist/views/index.d.ts.map +1 -1
  73. package/dist/views/index.js +3 -4
  74. package/dist/views/index.js.map +1 -1
  75. package/package.json +9 -9
  76. package/src/api/app/bsky/feed/searchPosts.ts +10 -8
  77. package/src/api/app/bsky/graph/getSuggestedFollowsByActor.ts +3 -1
  78. package/src/api/app/bsky/unspecced/getPostThreadV2.ts +3 -3
  79. package/src/api/app/bsky/unspecced/getSuggestedOnboardingUsers.ts +14 -7
  80. package/src/api/app/bsky/unspecced/getSuggestedUsers.ts +13 -6
  81. package/src/config.ts +8 -0
  82. package/src/context.ts +4 -4
  83. package/src/feature-gates/README.md +47 -0
  84. package/src/feature-gates/gates.ts +9 -0
  85. package/src/feature-gates/index.ts +146 -0
  86. package/src/feature-gates/metrics.test.ts +196 -0
  87. package/src/feature-gates/metrics.ts +107 -0
  88. package/src/feature-gates/types.ts +52 -0
  89. package/src/feature-gates/utils.ts +90 -0
  90. package/src/hydration/hydrator.ts +12 -6
  91. package/src/index.ts +8 -7
  92. package/src/lexicon/index.ts +13 -13
  93. package/src/lexicon/lexicons.ts +63 -55
  94. package/src/lexicon/types/app/bsky/graph/getSuggestedFollowsByActor.ts +3 -1
  95. package/src/lexicon/types/app/bsky/unspecced/{getSuggestedOnboardingUsersSkeleton.ts → getOnboardingSuggestedUsersSkeleton.ts} +1 -1
  96. package/src/lexicon/types/app/bsky/unspecced/getSuggestionsSkeleton.ts +3 -1
  97. package/src/views/index.ts +5 -8
  98. package/tests/views/get-suggested-onboarding-users.test.ts +1 -1
  99. package/tests/views/thread.test.ts +2 -0
  100. package/tsconfig.build.tsbuildinfo +1 -1
  101. package/dist/feature-gates.d.ts +0 -44
  102. package/dist/feature-gates.d.ts.map +0 -1
  103. package/dist/feature-gates.js +0 -133
  104. package/dist/feature-gates.js.map +0 -1
  105. package/src/feature-gates.ts +0 -136
@@ -1,133 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FeatureGates = exports.FeatureGateID = void 0;
4
- const growthbook_1 = require("@growthbook/growthbook");
5
- const logger_1 = require("./logger");
6
- /**
7
- * We want this to be sufficiently high that we don't time out under
8
- * normal conditions, but not so high that it takes too long to boot
9
- * the server.
10
- */
11
- const FETCH_TIMEOUT = 3e3; // 3 seconds
12
- /**
13
- * StatSig used to default to every 10s, but I think 1m is fine
14
- */
15
- const REFETCH_INTERVAL = 60e3; // 1 minute
16
- var FeatureGateID;
17
- (function (FeatureGateID) {
18
- /**
19
- * Left here ensure this is interpreted as a string enum and therefore
20
- * appease TS
21
- */
22
- FeatureGateID["_"] = "";
23
- FeatureGateID["SuggestedUsersDiscoverAgentEnable"] = "suggested_users:discover_agent:enable";
24
- FeatureGateID["SuggestedOnboardingUsersDiscoverAgentEnable"] = "suggested_onboarding_users:discover_agent:enable";
25
- FeatureGateID["ThreadsReplyRankingExplorationEnable"] = "threads:reply_ranking_exploration:enable";
26
- FeatureGateID["SearchFilteringExplorationEnable"] = "search:filtering_exploration:enable";
27
- })(FeatureGateID || (exports.FeatureGateID = FeatureGateID = {}));
28
- class FeatureGates {
29
- constructor(config) {
30
- Object.defineProperty(this, "config", {
31
- enumerable: true,
32
- configurable: true,
33
- writable: true,
34
- value: config
35
- });
36
- Object.defineProperty(this, "ready", {
37
- enumerable: true,
38
- configurable: true,
39
- writable: true,
40
- value: false
41
- });
42
- Object.defineProperty(this, "client", {
43
- enumerable: true,
44
- configurable: true,
45
- writable: true,
46
- value: undefined
47
- });
48
- Object.defineProperty(this, "ids", {
49
- enumerable: true,
50
- configurable: true,
51
- writable: true,
52
- value: FeatureGateID
53
- });
54
- Object.defineProperty(this, "refreshInterval", {
55
- enumerable: true,
56
- configurable: true,
57
- writable: true,
58
- value: undefined
59
- });
60
- }
61
- async start() {
62
- try {
63
- if (this.config.apiHost && this.config.clientKey) {
64
- this.client = new growthbook_1.GrowthBookClient({
65
- apiHost: this.config.apiHost,
66
- clientKey: this.config.clientKey,
67
- });
68
- const { source, error } = await this.client.init({
69
- timeout: FETCH_TIMEOUT,
70
- });
71
- /**
72
- * This does not necessarily mean that the client completely failed,
73
- * since it could just be that the request timed out. It may succeed
74
- * after the timeout, or later during refreshes.
75
- *
76
- * @see https://docs.growthbook.io/lib/node#error-handling
77
- */
78
- if (error) {
79
- logger_1.featureGatesLogger.error({ err: error, source }, 'Client failed to initialize normally');
80
- }
81
- /**
82
- * Set up periodic refresh of feature definitions
83
- *
84
- * @see https://docs.growthbook.io/lib/node#refreshing-features
85
- */
86
- this.refreshInterval = setInterval(async () => {
87
- try {
88
- await this.client?.refreshFeatures({
89
- timeout: FETCH_TIMEOUT,
90
- });
91
- }
92
- catch (err) {
93
- logger_1.featureGatesLogger.error({ err }, 'Failed to refresh features');
94
- }
95
- }, REFETCH_INTERVAL);
96
- /* Ready or not, here we come */
97
- this.ready = true;
98
- }
99
- else {
100
- logger_1.featureGatesLogger.error('Missing required config for FeatureGates client');
101
- }
102
- }
103
- catch (err) {
104
- logger_1.featureGatesLogger.error({ err }, 'Client initialization failed');
105
- this.ready = false;
106
- }
107
- }
108
- destroy() {
109
- if (this.ready) {
110
- this.ready = false;
111
- if (this.refreshInterval) {
112
- clearInterval(this.refreshInterval);
113
- }
114
- }
115
- }
116
- userContext({ did, }) {
117
- return { attributes: { did: did ?? null } };
118
- }
119
- check(gate, ctx) {
120
- if (!this.ready || !this.client)
121
- return false;
122
- return this.client.isOn(gate, ctx);
123
- }
124
- /**
125
- * Pre-evaluate multiple feature gates for a given user, returning a map of
126
- * gate ID to boolean result.
127
- */
128
- checkGates(gates, ctx) {
129
- return new Map(gates.map((g) => [g, this.check(g, ctx)]));
130
- }
131
- }
132
- exports.FeatureGates = FeatureGates;
133
- //# sourceMappingURL=feature-gates.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"feature-gates.js","sourceRoot":"","sources":["../src/feature-gates.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n GrowthBookClient,\n type UserContext as GrowthBookUserContext,\n} from '@growthbook/growthbook'\nimport { featureGatesLogger } from './logger'\n\n/**\n * We want this to be sufficiently high that we don't time out under\n * normal conditions, but not so high that it takes too long to boot\n * the server.\n */\nconst FETCH_TIMEOUT = 3e3 // 3 seconds\n\n/**\n * StatSig used to default to every 10s, but I think 1m is fine\n */\nconst REFETCH_INTERVAL = 60e3 // 1 minute\n\nexport type Config = {\n apiHost?: string\n clientKey?: string\n}\n\ntype UserContext = Omit<GrowthBookUserContext, 'attributes'> & {\n attributes?: {\n did?: string | null\n }\n}\n\nexport enum FeatureGateID {\n /**\n * Left here ensure this is interpreted as a string enum and therefore\n * appease TS\n */\n _ = '',\n SuggestedUsersDiscoverAgentEnable = 'suggested_users:discover_agent:enable',\n SuggestedOnboardingUsersDiscoverAgentEnable = 'suggested_onboarding_users:discover_agent:enable',\n ThreadsReplyRankingExplorationEnable = 'threads:reply_ranking_exploration:enable',\n SearchFilteringExplorationEnable = 'search:filtering_exploration:enable',\n}\n\n/**\n * Pre-evaluated feature gates map, the result of `FeatureGates.checkGates()`\n */\nexport type CheckedFeatureGatesMap = Map<FeatureGateID, boolean>\n\nexport class FeatureGates {\n ready = false\n client: GrowthBookClient | undefined = undefined\n ids = FeatureGateID\n refreshInterval: NodeJS.Timeout | undefined = undefined\n\n constructor(private config: Config) {}\n\n async start() {\n try {\n if (this.config.apiHost && this.config.clientKey) {\n this.client = new GrowthBookClient({\n apiHost: this.config.apiHost,\n clientKey: this.config.clientKey,\n })\n\n const { source, error } = await this.client.init({\n timeout: FETCH_TIMEOUT,\n })\n\n /**\n * This does not necessarily mean that the client completely failed,\n * since it could just be that the request timed out. It may succeed\n * after the timeout, or later during refreshes.\n *\n * @see https://docs.growthbook.io/lib/node#error-handling\n */\n if (error) {\n featureGatesLogger.error(\n { err: error, source },\n 'Client failed to initialize normally',\n )\n }\n\n /**\n * Set up periodic refresh of feature definitions\n *\n * @see https://docs.growthbook.io/lib/node#refreshing-features\n */\n this.refreshInterval = setInterval(async () => {\n try {\n await this.client?.refreshFeatures({\n timeout: FETCH_TIMEOUT,\n })\n } catch (err) {\n featureGatesLogger.error({ err }, 'Failed to refresh features')\n }\n }, REFETCH_INTERVAL)\n\n /* Ready or not, here we come */\n this.ready = true\n } else {\n featureGatesLogger.error(\n 'Missing required config for FeatureGates client',\n )\n }\n } catch (err) {\n featureGatesLogger.error({ err }, 'Client initialization failed')\n this.ready = false\n }\n }\n\n destroy() {\n if (this.ready) {\n this.ready = false\n if (this.refreshInterval) {\n clearInterval(this.refreshInterval)\n }\n }\n }\n\n userContext({\n did,\n }: Exclude<UserContext['attributes'], undefined>): UserContext {\n return { attributes: { did: did ?? null } }\n }\n\n check(gate: FeatureGateID, ctx: UserContext): boolean {\n if (!this.ready || !this.client) return false\n return this.client.isOn(gate, ctx)\n }\n\n /**\n * Pre-evaluate multiple feature gates for a given user, returning a map of\n * gate ID to boolean result.\n */\n checkGates(gates: FeatureGateID[], ctx: UserContext): CheckedFeatureGatesMap {\n return new Map(gates.map((g) => [g, this.check(g, ctx)]))\n }\n}\n"]}
@@ -1,136 +0,0 @@
1
- import {
2
- GrowthBookClient,
3
- type UserContext as GrowthBookUserContext,
4
- } from '@growthbook/growthbook'
5
- import { featureGatesLogger } from './logger'
6
-
7
- /**
8
- * We want this to be sufficiently high that we don't time out under
9
- * normal conditions, but not so high that it takes too long to boot
10
- * the server.
11
- */
12
- const FETCH_TIMEOUT = 3e3 // 3 seconds
13
-
14
- /**
15
- * StatSig used to default to every 10s, but I think 1m is fine
16
- */
17
- const REFETCH_INTERVAL = 60e3 // 1 minute
18
-
19
- export type Config = {
20
- apiHost?: string
21
- clientKey?: string
22
- }
23
-
24
- type UserContext = Omit<GrowthBookUserContext, 'attributes'> & {
25
- attributes?: {
26
- did?: string | null
27
- }
28
- }
29
-
30
- export enum FeatureGateID {
31
- /**
32
- * Left here ensure this is interpreted as a string enum and therefore
33
- * appease TS
34
- */
35
- _ = '',
36
- SuggestedUsersDiscoverAgentEnable = 'suggested_users:discover_agent:enable',
37
- SuggestedOnboardingUsersDiscoverAgentEnable = 'suggested_onboarding_users:discover_agent:enable',
38
- ThreadsReplyRankingExplorationEnable = 'threads:reply_ranking_exploration:enable',
39
- SearchFilteringExplorationEnable = 'search:filtering_exploration:enable',
40
- }
41
-
42
- /**
43
- * Pre-evaluated feature gates map, the result of `FeatureGates.checkGates()`
44
- */
45
- export type CheckedFeatureGatesMap = Map<FeatureGateID, boolean>
46
-
47
- export class FeatureGates {
48
- ready = false
49
- client: GrowthBookClient | undefined = undefined
50
- ids = FeatureGateID
51
- refreshInterval: NodeJS.Timeout | undefined = undefined
52
-
53
- constructor(private config: Config) {}
54
-
55
- async start() {
56
- try {
57
- if (this.config.apiHost && this.config.clientKey) {
58
- this.client = new GrowthBookClient({
59
- apiHost: this.config.apiHost,
60
- clientKey: this.config.clientKey,
61
- })
62
-
63
- const { source, error } = await this.client.init({
64
- timeout: FETCH_TIMEOUT,
65
- })
66
-
67
- /**
68
- * This does not necessarily mean that the client completely failed,
69
- * since it could just be that the request timed out. It may succeed
70
- * after the timeout, or later during refreshes.
71
- *
72
- * @see https://docs.growthbook.io/lib/node#error-handling
73
- */
74
- if (error) {
75
- featureGatesLogger.error(
76
- { err: error, source },
77
- 'Client failed to initialize normally',
78
- )
79
- }
80
-
81
- /**
82
- * Set up periodic refresh of feature definitions
83
- *
84
- * @see https://docs.growthbook.io/lib/node#refreshing-features
85
- */
86
- this.refreshInterval = setInterval(async () => {
87
- try {
88
- await this.client?.refreshFeatures({
89
- timeout: FETCH_TIMEOUT,
90
- })
91
- } catch (err) {
92
- featureGatesLogger.error({ err }, 'Failed to refresh features')
93
- }
94
- }, REFETCH_INTERVAL)
95
-
96
- /* Ready or not, here we come */
97
- this.ready = true
98
- } else {
99
- featureGatesLogger.error(
100
- 'Missing required config for FeatureGates client',
101
- )
102
- }
103
- } catch (err) {
104
- featureGatesLogger.error({ err }, 'Client initialization failed')
105
- this.ready = false
106
- }
107
- }
108
-
109
- destroy() {
110
- if (this.ready) {
111
- this.ready = false
112
- if (this.refreshInterval) {
113
- clearInterval(this.refreshInterval)
114
- }
115
- }
116
- }
117
-
118
- userContext({
119
- did,
120
- }: Exclude<UserContext['attributes'], undefined>): UserContext {
121
- return { attributes: { did: did ?? null } }
122
- }
123
-
124
- check(gate: FeatureGateID, ctx: UserContext): boolean {
125
- if (!this.ready || !this.client) return false
126
- return this.client.isOn(gate, ctx)
127
- }
128
-
129
- /**
130
- * Pre-evaluate multiple feature gates for a given user, returning a map of
131
- * gate ID to boolean result.
132
- */
133
- checkGates(gates: FeatureGateID[], ctx: UserContext): CheckedFeatureGatesMap {
134
- return new Map(gates.map((g) => [g, this.check(g, ctx)]))
135
- }
136
- }