graze-client 0.8.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/dts/GrazeClient.d.ts +368 -242
- package/build/dts/GrazeClient.d.ts.map +1 -1
- package/build/dts/builder.d.ts +2 -2
- package/build/dts/builder.d.ts.map +1 -1
- package/build/dts/common.d.ts +2 -3
- package/build/dts/common.d.ts.map +1 -1
- package/build/dts/index.d.ts +12 -12
- package/build/dts/index.d.ts.map +1 -1
- package/build/dts/schema/Post.d.ts +52 -52
- package/build/dts/schema/Post.d.ts.map +1 -1
- package/build/dts/schema/StickyPosts.d.ts +55 -56
- package/build/dts/schema/StickyPosts.d.ts.map +1 -1
- package/build/dts/schema/UpdateAlgorithmPayload.d.ts +137 -137
- package/build/dts/schema/UpdateAlgorithmPayload.d.ts.map +1 -1
- package/build/dts/schema/index.d.ts.map +1 -1
- package/build/dts/schema/shared.d.ts +126 -126
- package/build/dts/schema/shared.d.ts.map +1 -1
- package/build/dts/stickyposts.d.ts +80 -78
- package/build/dts/stickyposts.d.ts.map +1 -1
- package/build/esm/GrazeClient.js +47 -31
- package/build/esm/GrazeClient.js.map +1 -1
- package/build/esm/builder.js +20 -20
- package/build/esm/builder.js.map +1 -1
- package/build/esm/common.js +4 -3
- package/build/esm/common.js.map +1 -1
- package/build/esm/index.js +11 -8
- package/build/esm/index.js.map +1 -1
- package/build/esm/schema/Post.js +9 -9
- package/build/esm/schema/Post.js.map +1 -1
- package/build/esm/schema/StickyPosts.js +8 -9
- package/build/esm/schema/StickyPosts.js.map +1 -1
- package/build/esm/schema/UpdateAlgorithmPayload.js +130 -53
- package/build/esm/schema/UpdateAlgorithmPayload.js.map +1 -1
- package/build/esm/schema/index.js.map +1 -1
- package/build/esm/schema/shared.js +14 -14
- package/build/esm/schema/shared.js.map +1 -1
- package/build/esm/stickyposts.js +28 -13
- package/build/esm/stickyposts.js.map +1 -1
- package/package.json +21 -23
- package/src/GrazeClient.ts +83 -76
- package/src/builder.ts +78 -74
- package/src/common.ts +5 -10
- package/src/index.ts +61 -52
- package/src/schema/Post.ts +16 -16
- package/src/schema/StickyPosts.ts +22 -26
- package/src/schema/UpdateAlgorithmPayload.ts +153 -145
- package/src/schema/index.ts +2 -2
- package/src/schema/shared.ts +32 -32
- package/src/stickyposts.ts +48 -27
package/src/GrazeClient.ts
CHANGED
|
@@ -1,90 +1,92 @@
|
|
|
1
|
-
import {
|
|
2
|
-
FetchHttpClient,
|
|
3
|
-
HttpApi,
|
|
4
|
-
HttpApiClient,
|
|
5
|
-
HttpApiEndpoint,
|
|
6
|
-
HttpApiGroup,
|
|
7
|
-
HttpApiSchema,
|
|
8
|
-
HttpClient,
|
|
9
|
-
HttpClientRequest
|
|
10
|
-
} from "@effect/platform"
|
|
11
|
-
import { Config, Data, Effect, Redacted, Schema } from "effect"
|
|
1
|
+
import { Config, Context, Data, Effect, Layer, Redacted, Schema } from "effect";
|
|
12
2
|
import {
|
|
13
3
|
AlgoUpdateResponse,
|
|
14
4
|
GetAlgoResponse,
|
|
15
5
|
GetAlgorithmsResponse,
|
|
16
6
|
HidePostBody,
|
|
17
|
-
UnhidePostBody
|
|
18
|
-
} from "./schema/shared.js"
|
|
7
|
+
UnhidePostBody,
|
|
8
|
+
} from "./schema/shared.js";
|
|
19
9
|
|
|
20
|
-
import { FeedIdParam, UserIdParam } from "./common.js"
|
|
21
|
-
import * as Form from "./schema/UpdateAlgorithmPayload.js"
|
|
22
|
-
import { StickyPostsApiGroup } from "./stickyposts.js"
|
|
10
|
+
import { FeedIdParam, UserIdParam } from "./common.js";
|
|
11
|
+
import * as Form from "./schema/UpdateAlgorithmPayload.js";
|
|
12
|
+
import { StickyPostsApiGroup } from "./stickyposts.js";
|
|
13
|
+
import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient";
|
|
14
|
+
import * as HttpApi from "effect/unstable/httpapi/HttpApi";
|
|
15
|
+
import * as HttpApiClient from "effect/unstable/httpapi/HttpApiClient";
|
|
16
|
+
import * as HttpApiEndpoint from "effect/unstable/httpapi/HttpApiEndpoint";
|
|
17
|
+
import * as HttpApiGroup from "effect/unstable/httpapi/HttpApiGroup";
|
|
18
|
+
import * as HttpApiSchema from "effect/unstable/httpapi/HttpApiSchema";
|
|
19
|
+
import * as HttpClient from "effect/unstable/http/HttpClient";
|
|
20
|
+
import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest";
|
|
23
21
|
|
|
24
22
|
function isStringOrBlob(val: unknown): val is string | Blob {
|
|
25
|
-
return typeof val === "string" || val instanceof Blob
|
|
23
|
+
return typeof val === "string" || val instanceof Blob;
|
|
26
24
|
}
|
|
27
25
|
|
|
28
26
|
const encodeForm = (obj: Form.FullForm) =>
|
|
29
|
-
Effect.gen(function*() {
|
|
30
|
-
const encoded = yield* Schema.
|
|
31
|
-
const formData = new FormData()
|
|
27
|
+
Effect.gen(function* () {
|
|
28
|
+
const encoded = yield* Schema.encodeEffect(Form.FullForm)(obj);
|
|
29
|
+
const formData = new FormData();
|
|
32
30
|
for (const key in encoded) {
|
|
33
31
|
if (Object.prototype.hasOwnProperty.call(encoded, key)) {
|
|
34
|
-
const val = encoded[key as keyof Form.FullForm]
|
|
32
|
+
const val = encoded[key as keyof Form.FullForm];
|
|
35
33
|
if (isStringOrBlob(val)) {
|
|
36
|
-
formData.append(key, val)
|
|
34
|
+
formData.append(key, val);
|
|
37
35
|
}
|
|
38
36
|
}
|
|
39
37
|
}
|
|
40
|
-
return formData
|
|
41
|
-
})
|
|
38
|
+
return formData;
|
|
39
|
+
});
|
|
42
40
|
|
|
43
41
|
const GrazeApiGroup = HttpApiGroup.make("graze", { topLevel: true })
|
|
44
42
|
.add(
|
|
45
|
-
HttpApiEndpoint.get("getFeeds"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
43
|
+
HttpApiEndpoint.get("getFeeds", "/app/my_feeds", {
|
|
44
|
+
// v3 GET `setPayload` serialised to query params (GET has no body); v4
|
|
45
|
+
// expresses that as `query`.
|
|
46
|
+
query: Schema.Struct({
|
|
47
|
+
user_id: UserIdParam,
|
|
48
|
+
}),
|
|
49
|
+
success: GetAlgorithmsResponse,
|
|
50
|
+
}),
|
|
52
51
|
)
|
|
53
52
|
.add(
|
|
54
|
-
HttpApiEndpoint.get("getFeed"
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
HttpApiEndpoint.get("getFeed", "/app/my_feeds/:feedId", {
|
|
54
|
+
params: { feedId: FeedIdParam },
|
|
55
|
+
success: GetAlgoResponse,
|
|
56
|
+
}),
|
|
57
57
|
)
|
|
58
58
|
.add(
|
|
59
|
-
HttpApiEndpoint.post("updateAlgorithm"
|
|
60
|
-
.
|
|
61
|
-
|
|
59
|
+
HttpApiEndpoint.post("updateAlgorithm", "/app/edit_algo_image", {
|
|
60
|
+
payload: Form.FullForm.pipe(HttpApiSchema.asMultipart()),
|
|
61
|
+
success: AlgoUpdateResponse,
|
|
62
|
+
}),
|
|
62
63
|
)
|
|
63
64
|
.add(
|
|
64
|
-
HttpApiEndpoint.get(
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
HttpApiEndpoint.get("publishAlgorithm", "/app/publish_algo/:feedId", {
|
|
66
|
+
params: { feedId: FeedIdParam },
|
|
67
|
+
success: Schema.Unknown,
|
|
68
|
+
}),
|
|
67
69
|
)
|
|
68
70
|
.add(
|
|
69
|
-
HttpApiEndpoint.post("hidePost"
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
HttpApiEndpoint.post("hidePost", "/app/hide_post", {
|
|
72
|
+
payload: HidePostBody,
|
|
73
|
+
success: Schema.Unknown,
|
|
74
|
+
}),
|
|
72
75
|
)
|
|
73
76
|
.add(
|
|
74
|
-
HttpApiEndpoint.post("unhidePost"
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
HttpApiEndpoint.post("unhidePost", "/app/unhide_post", {
|
|
78
|
+
payload: UnhidePostBody,
|
|
79
|
+
success: Schema.Unknown,
|
|
80
|
+
}),
|
|
81
|
+
);
|
|
78
82
|
|
|
79
83
|
class GrazeApiDef extends HttpApi.make("grazeApi")
|
|
80
84
|
.add(GrazeApiGroup)
|
|
81
|
-
.add(StickyPostsApiGroup)
|
|
82
|
-
.addError(Schema.Unknown)
|
|
83
|
-
{}
|
|
85
|
+
.add(StickyPostsApiGroup) {}
|
|
84
86
|
|
|
85
|
-
const grazeLive = Effect.gen(function*() {
|
|
86
|
-
const baseUrl = yield* Config.url("GRAZE_API_URL")
|
|
87
|
-
const cookie = yield* Config.redacted("GRAZE_COOKIE")
|
|
87
|
+
const grazeLive = Effect.gen(function* () {
|
|
88
|
+
const baseUrl = yield* Config.url("GRAZE_API_URL");
|
|
89
|
+
const cookie = yield* Config.redacted("GRAZE_COOKIE");
|
|
88
90
|
|
|
89
91
|
const client = yield* HttpApiClient.make(GrazeApiDef, {
|
|
90
92
|
baseUrl: baseUrl.toString(),
|
|
@@ -93,38 +95,43 @@ const grazeLive = Effect.gen(function*() {
|
|
|
93
95
|
HttpClient.filterStatusOk,
|
|
94
96
|
HttpClient.mapRequest(
|
|
95
97
|
HttpClientRequest.setHeaders({
|
|
96
|
-
cookie: Redacted.value(cookie)
|
|
97
|
-
})
|
|
98
|
-
)
|
|
99
|
-
)
|
|
100
|
-
})
|
|
98
|
+
cookie: Redacted.value(cookie),
|
|
99
|
+
}),
|
|
100
|
+
),
|
|
101
|
+
),
|
|
102
|
+
});
|
|
101
103
|
|
|
102
104
|
const updateAlgorithm = (data: Form.FullForm) =>
|
|
103
|
-
Effect.gen(function*() {
|
|
104
|
-
const formData = yield* encodeForm(data)
|
|
105
|
-
return yield* client.updateAlgorithm({ payload: formData })
|
|
106
|
-
})
|
|
105
|
+
Effect.gen(function* () {
|
|
106
|
+
const formData = yield* encodeForm(data);
|
|
107
|
+
return yield* client.updateAlgorithm({ payload: formData });
|
|
108
|
+
});
|
|
107
109
|
|
|
108
|
-
const userId = yield* Config.
|
|
109
|
-
const getFeeds = () => client.getFeeds({
|
|
110
|
+
const userId = yield* Config.int("GRAZE_USER_ID");
|
|
111
|
+
const getFeeds = () => client.getFeeds({ query: { user_id: userId } });
|
|
110
112
|
|
|
111
113
|
return {
|
|
112
114
|
...client,
|
|
113
115
|
updateAlgorithm,
|
|
114
|
-
getFeeds
|
|
115
|
-
}
|
|
116
|
-
})
|
|
116
|
+
getFeeds,
|
|
117
|
+
};
|
|
118
|
+
});
|
|
117
119
|
|
|
118
|
-
export class GrazeClient extends
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
120
|
+
export class GrazeClient extends Context.Service<
|
|
121
|
+
GrazeClient,
|
|
122
|
+
Effect.Success<typeof grazeLive>
|
|
123
|
+
>()("GrazeClient") {
|
|
124
|
+
static readonly layer = Layer.effect(GrazeClient, grazeLive).pipe(
|
|
125
|
+
Layer.provide(FetchHttpClient.layer),
|
|
126
|
+
);
|
|
123
127
|
}
|
|
124
128
|
|
|
129
|
+
/** The resolved service shape (v4 has no `.Service` namespace on the tag). */
|
|
130
|
+
export type GrazeClientService = Effect.Success<typeof grazeLive>;
|
|
131
|
+
|
|
125
132
|
export class GrazeError extends Data.TaggedError("GrazeError")<{
|
|
126
|
-
message: string
|
|
127
|
-
status: number
|
|
128
|
-
statusText: string
|
|
129
|
-
json: unknown
|
|
133
|
+
message: string;
|
|
134
|
+
status: number;
|
|
135
|
+
statusText: string;
|
|
136
|
+
json: unknown;
|
|
130
137
|
}> {}
|
package/src/builder.ts
CHANGED
|
@@ -11,144 +11,148 @@ import type {
|
|
|
11
11
|
RegexNegationMatches,
|
|
12
12
|
SocialGraph,
|
|
13
13
|
TextMatchesAny,
|
|
14
|
-
TextMatchesNone
|
|
15
|
-
} from "./schema/index.js"
|
|
14
|
+
TextMatchesNone,
|
|
15
|
+
} from "./schema/index.js";
|
|
16
16
|
|
|
17
17
|
const neverMatch: Filter = {
|
|
18
|
-
or: []
|
|
19
|
-
}
|
|
18
|
+
or: [],
|
|
19
|
+
};
|
|
20
20
|
|
|
21
21
|
const and = (...filters: Array<Filter | null>): Filter => {
|
|
22
|
-
const flatFilters: Array<Filter> = []
|
|
22
|
+
const flatFilters: Array<Filter> = [];
|
|
23
23
|
filters.forEach((f) => {
|
|
24
24
|
if (f === null) {
|
|
25
25
|
// if the filter is null, we never want to match this block.
|
|
26
|
-
flatFilters.push(neverMatch)
|
|
26
|
+
flatFilters.push(neverMatch);
|
|
27
27
|
} else if ("and" in f) {
|
|
28
28
|
// Automatically flatten nested ands.
|
|
29
|
-
flatFilters.push(...f.and)
|
|
29
|
+
flatFilters.push(...f.and);
|
|
30
30
|
} else {
|
|
31
|
-
flatFilters.push(f)
|
|
31
|
+
flatFilters.push(f);
|
|
32
32
|
}
|
|
33
|
-
})
|
|
34
|
-
return { and: flatFilters }
|
|
35
|
-
}
|
|
33
|
+
});
|
|
34
|
+
return { and: flatFilters };
|
|
35
|
+
};
|
|
36
36
|
const or = (...filters: Array<Filter | null>): Filter => {
|
|
37
|
-
const flatFilters: Array<Filter> = []
|
|
37
|
+
const flatFilters: Array<Filter> = [];
|
|
38
38
|
filters
|
|
39
39
|
.filter((f) => f !== null)
|
|
40
40
|
.forEach((f) => {
|
|
41
41
|
if ("or" in f) {
|
|
42
|
-
flatFilters.push(...f.or)
|
|
42
|
+
flatFilters.push(...f.or);
|
|
43
43
|
} else {
|
|
44
|
-
flatFilters.push(f)
|
|
44
|
+
flatFilters.push(f);
|
|
45
45
|
}
|
|
46
|
-
})
|
|
47
|
-
return { or: flatFilters }
|
|
48
|
-
}
|
|
46
|
+
});
|
|
47
|
+
return { or: flatFilters };
|
|
48
|
+
};
|
|
49
49
|
|
|
50
50
|
const regexMatches = (
|
|
51
51
|
attribute: RegexMatches["regex_matches"][0],
|
|
52
|
-
regex: string
|
|
52
|
+
regex: string,
|
|
53
53
|
): RegexMatches => ({
|
|
54
|
-
regex_matches: [attribute, regex, true]
|
|
55
|
-
})
|
|
54
|
+
regex_matches: [attribute, regex, true],
|
|
55
|
+
});
|
|
56
56
|
|
|
57
57
|
const regexNegationMatches = (
|
|
58
58
|
attribute: RegexNegationMatches["regex_negation_matches"][0],
|
|
59
|
-
regex: string
|
|
59
|
+
regex: string,
|
|
60
60
|
): RegexNegationMatches => ({
|
|
61
|
-
regex_negation_matches: [attribute, regex, true]
|
|
62
|
-
})
|
|
61
|
+
regex_negation_matches: [attribute, regex, true],
|
|
62
|
+
});
|
|
63
63
|
|
|
64
64
|
const textMatchesAny = ({
|
|
65
65
|
attribute,
|
|
66
66
|
caseInsensitive = true,
|
|
67
67
|
patterns,
|
|
68
|
-
useRegex = false
|
|
68
|
+
useRegex = false,
|
|
69
69
|
}: {
|
|
70
|
-
attribute: AttributeName
|
|
71
|
-
patterns: Array<string
|
|
72
|
-
caseInsensitive?: boolean
|
|
73
|
-
useRegex?: boolean
|
|
70
|
+
attribute: AttributeName;
|
|
71
|
+
patterns: Array<string>;
|
|
72
|
+
caseInsensitive?: boolean;
|
|
73
|
+
useRegex?: boolean;
|
|
74
74
|
}): TextMatchesAny => ({
|
|
75
|
-
regex_any: [attribute, patterns, caseInsensitive, useRegex]
|
|
76
|
-
})
|
|
75
|
+
regex_any: [attribute, patterns, caseInsensitive, useRegex],
|
|
76
|
+
});
|
|
77
77
|
|
|
78
78
|
const textMatchesNone = ({
|
|
79
79
|
attribute,
|
|
80
80
|
caseInsensitive = true,
|
|
81
81
|
patterns,
|
|
82
|
-
useRegex = false
|
|
82
|
+
useRegex = false,
|
|
83
83
|
}: {
|
|
84
|
-
attribute: AttributeName
|
|
85
|
-
patterns: Array<string
|
|
86
|
-
caseInsensitive?: boolean
|
|
87
|
-
useRegex?: boolean
|
|
84
|
+
attribute: AttributeName;
|
|
85
|
+
patterns: Array<string>;
|
|
86
|
+
caseInsensitive?: boolean;
|
|
87
|
+
useRegex?: boolean;
|
|
88
88
|
}): TextMatchesNone => ({
|
|
89
|
-
regex_none: [attribute, patterns, caseInsensitive, useRegex]
|
|
90
|
-
})
|
|
89
|
+
regex_none: [attribute, patterns, caseInsensitive, useRegex],
|
|
90
|
+
});
|
|
91
91
|
|
|
92
92
|
const isListMember = (uri: string): ListMemberFilter => ({
|
|
93
|
-
list_member: [uri, "in"]
|
|
94
|
-
})
|
|
93
|
+
list_member: [uri, "in"],
|
|
94
|
+
});
|
|
95
95
|
const isNotListMember = (uri: string): ListMemberFilter => ({
|
|
96
|
-
list_member: [uri, "not_in"]
|
|
97
|
-
})
|
|
96
|
+
list_member: [uri, "not_in"],
|
|
97
|
+
});
|
|
98
98
|
const socialGraph = (
|
|
99
99
|
identifier: string,
|
|
100
100
|
op: SocialGraph["social_graph"][1],
|
|
101
|
-
relation: SocialGraph["social_graph"][2]
|
|
101
|
+
relation: SocialGraph["social_graph"][2],
|
|
102
102
|
): SocialGraph => ({
|
|
103
|
-
social_graph: [identifier, op, relation]
|
|
104
|
-
})
|
|
105
|
-
const posterIsFollowedBy = (identifier: string) =>
|
|
106
|
-
|
|
107
|
-
const
|
|
108
|
-
|
|
103
|
+
social_graph: [identifier, op, relation],
|
|
104
|
+
});
|
|
105
|
+
const posterIsFollowedBy = (identifier: string) =>
|
|
106
|
+
socialGraph(identifier, "in", "followers");
|
|
107
|
+
const posterFollows = (identifier: string) =>
|
|
108
|
+
socialGraph(identifier, "in", "follows");
|
|
109
|
+
const posterIsNotFollowedBy = (identifier: string) =>
|
|
110
|
+
socialGraph(identifier, "not_in", "followers");
|
|
111
|
+
const posterDoesNotFollow = (identifier: string) =>
|
|
112
|
+
socialGraph(identifier, "not_in", "follows");
|
|
109
113
|
|
|
110
114
|
const embedContains = (type: EmbedType["embed_type"][1]): Filter => ({
|
|
111
|
-
embed_type: ["==", type]
|
|
112
|
-
})
|
|
115
|
+
embed_type: ["==", type],
|
|
116
|
+
});
|
|
113
117
|
const embedDoesNotContain = (type: EmbedType["embed_type"][1]): Filter => ({
|
|
114
|
-
embed_type: ["!=", type]
|
|
115
|
-
})
|
|
118
|
+
embed_type: ["!=", type],
|
|
119
|
+
});
|
|
116
120
|
|
|
117
121
|
const postTypeIs = (...types: PostTypeFilter["post_type"][1]): Filter => ({
|
|
118
|
-
post_type: ["in", types]
|
|
119
|
-
})
|
|
122
|
+
post_type: ["in", types],
|
|
123
|
+
});
|
|
120
124
|
const postTypeIsNot = (...types: PostTypeFilter["post_type"][1]): Filter => ({
|
|
121
|
-
post_type: ["not_in", types]
|
|
122
|
-
})
|
|
125
|
+
post_type: ["not_in", types],
|
|
126
|
+
});
|
|
123
127
|
|
|
124
|
-
const hasVideo: Filter = embedContains("video")
|
|
128
|
+
const hasVideo: Filter = embedContains("video");
|
|
125
129
|
|
|
126
130
|
const hasNoNSFWLabels: Filter = {
|
|
127
|
-
entity_excludes: ["labels", ["porn", "sexual", "nudity"]]
|
|
128
|
-
}
|
|
131
|
+
entity_excludes: ["labels", ["porn", "sexual", "nudity"]],
|
|
132
|
+
};
|
|
129
133
|
const attributeCompare = (
|
|
130
134
|
attr: AttributeName,
|
|
131
135
|
op: AttributeComparison,
|
|
132
|
-
value: AttributeValue
|
|
136
|
+
value: AttributeValue,
|
|
133
137
|
): Filter => ({
|
|
134
|
-
attribute_compare: [attr, op, value]
|
|
135
|
-
})
|
|
138
|
+
attribute_compare: [attr, op, value],
|
|
139
|
+
});
|
|
136
140
|
|
|
137
|
-
const isPost: Filter = postTypeIsNot("reply", "quote")
|
|
138
|
-
const isReply: Filter = postTypeIs("reply")
|
|
139
|
-
const isQuote: Filter = postTypeIs("quote")
|
|
140
|
-
const isNotReply: Filter = postTypeIsNot("reply")
|
|
141
|
-
const isNotQuote: Filter = postTypeIsNot("quote")
|
|
141
|
+
const isPost: Filter = postTypeIsNot("reply", "quote");
|
|
142
|
+
const isReply: Filter = postTypeIs("reply");
|
|
143
|
+
const isQuote: Filter = postTypeIs("quote");
|
|
144
|
+
const isNotReply: Filter = postTypeIsNot("reply");
|
|
145
|
+
const isNotQuote: Filter = postTypeIsNot("quote");
|
|
142
146
|
|
|
143
147
|
const isContentOk = (threshold: number = 0.9): ContentModerationBlock => ({
|
|
144
|
-
content_moderation: ["OK", ">=", threshold]
|
|
145
|
-
})
|
|
148
|
+
content_moderation: ["OK", ">=", threshold],
|
|
149
|
+
});
|
|
146
150
|
|
|
147
151
|
export const Algo = {
|
|
148
152
|
filter: (...filters: Array<Filter>): { filter: Filter } => ({
|
|
149
|
-
filter: and(...filters)
|
|
150
|
-
})
|
|
151
|
-
}
|
|
153
|
+
filter: and(...filters),
|
|
154
|
+
}),
|
|
155
|
+
};
|
|
152
156
|
|
|
153
157
|
export const F = {
|
|
154
158
|
and,
|
|
@@ -176,5 +180,5 @@ export const F = {
|
|
|
176
180
|
isNotQuote,
|
|
177
181
|
isContentOk,
|
|
178
182
|
embedContains,
|
|
179
|
-
embedDoesNotContain
|
|
180
|
-
}
|
|
183
|
+
embedDoesNotContain,
|
|
184
|
+
};
|
package/src/common.ts
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Schema } from "effect"
|
|
1
|
+
import { Schema } from "effect";
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export const FeedIdParam = HttpApiSchema.param(
|
|
9
|
-
"feedId",
|
|
10
|
-
Schema.NumberFromString
|
|
11
|
-
)
|
|
3
|
+
// v4 has no `HttpApiSchema.param`: path params are declared inline in the
|
|
4
|
+
// endpoint path (`:feedId`) with a `params: { feedId: FeedIdParam }` schema map.
|
|
5
|
+
export const UserIdParam = Schema.NumberFromString;
|
|
6
|
+
export const FeedIdParam = Schema.NumberFromString;
|
package/src/index.ts
CHANGED
|
@@ -1,85 +1,94 @@
|
|
|
1
|
-
import { ConfigProvider, Effect
|
|
2
|
-
import { GrazeClient } from "./GrazeClient.js"
|
|
1
|
+
import { ConfigProvider, Effect } from "effect";
|
|
2
|
+
import { GrazeClient, type GrazeClientService } from "./GrazeClient.js";
|
|
3
3
|
|
|
4
4
|
type Promisify<T> = T extends (
|
|
5
5
|
...args: Array<any>
|
|
6
|
-
) => Effect.Effect<infer A, infer _E, never>
|
|
7
|
-
:
|
|
6
|
+
) => Effect.Effect<infer A, infer _E, never>
|
|
7
|
+
? (...args: Parameters<T>) => Promise<A>
|
|
8
|
+
: never;
|
|
8
9
|
|
|
9
10
|
export class GrazeService {
|
|
10
|
-
getFeed: Promisify<
|
|
11
|
-
getFeeds: Promisify<
|
|
12
|
-
hidePost: Promisify<
|
|
13
|
-
unhidePost: Promisify<
|
|
14
|
-
updateAlgorithm: Promisify<
|
|
15
|
-
publishAlgorithm: Promisify<
|
|
11
|
+
getFeed: Promisify<GrazeClientService["getFeed"]>;
|
|
12
|
+
getFeeds: Promisify<GrazeClientService["getFeeds"]>;
|
|
13
|
+
hidePost: Promisify<GrazeClientService["hidePost"]>;
|
|
14
|
+
unhidePost: Promisify<GrazeClientService["unhidePost"]>;
|
|
15
|
+
updateAlgorithm: Promisify<GrazeClientService["updateAlgorithm"]>;
|
|
16
|
+
publishAlgorithm: Promisify<GrazeClientService["publishAlgorithm"]>;
|
|
16
17
|
stickyposts: {
|
|
17
|
-
get: Promisify<
|
|
18
|
-
create: Promisify<
|
|
19
|
-
delete: Promisify<
|
|
20
|
-
update: Promisify<
|
|
21
|
-
}
|
|
18
|
+
get: Promisify<GrazeClientService["stickyposts"]["get"]>;
|
|
19
|
+
create: Promisify<GrazeClientService["stickyposts"]["create"]>;
|
|
20
|
+
delete: Promisify<GrazeClientService["stickyposts"]["delete"]>;
|
|
21
|
+
update: Promisify<GrazeClientService["stickyposts"]["update"]>;
|
|
22
|
+
};
|
|
22
23
|
|
|
23
24
|
constructor({
|
|
24
25
|
apiUrl,
|
|
25
26
|
cookie,
|
|
26
|
-
userId
|
|
27
|
+
userId,
|
|
27
28
|
}: {
|
|
28
|
-
apiUrl: string
|
|
29
|
-
cookie: string
|
|
30
|
-
userId: number
|
|
29
|
+
apiUrl: string;
|
|
30
|
+
cookie: string;
|
|
31
|
+
userId: number;
|
|
31
32
|
}) {
|
|
32
33
|
const envMap = new Map([
|
|
33
34
|
["GRAZE_API_URL", apiUrl],
|
|
34
35
|
["GRAZE_COOKIE", cookie],
|
|
35
|
-
["GRAZE_USER_ID", userId.toString()]
|
|
36
|
-
])
|
|
37
|
-
const provider = ConfigProvider.
|
|
38
|
-
|
|
36
|
+
["GRAZE_USER_ID", userId.toString()],
|
|
37
|
+
]);
|
|
38
|
+
const provider = ConfigProvider.make((path) => {
|
|
39
|
+
const value = envMap.get(path.join("."));
|
|
40
|
+
return Effect.succeed(
|
|
41
|
+
value !== undefined ? ConfigProvider.makeValue(value) : undefined,
|
|
42
|
+
);
|
|
43
|
+
});
|
|
44
|
+
const layer = ConfigProvider.layer(provider);
|
|
39
45
|
const make = setup.pipe(
|
|
40
|
-
Effect.provide(GrazeClient.
|
|
41
|
-
Effect.provide(layer)
|
|
42
|
-
)
|
|
43
|
-
const api = Effect.runSync(make)
|
|
44
|
-
this.getFeed = api.getFeed
|
|
45
|
-
this.getFeeds = api.getFeeds
|
|
46
|
-
this.hidePost = api.hidePost
|
|
47
|
-
this.unhidePost = api.unhidePost
|
|
48
|
-
this.updateAlgorithm = api.updateAlgorithm
|
|
49
|
-
this.publishAlgorithm = api.publishAlgorithm
|
|
46
|
+
Effect.provide(GrazeClient.layer),
|
|
47
|
+
Effect.provide(layer),
|
|
48
|
+
);
|
|
49
|
+
const api = Effect.runSync(make);
|
|
50
|
+
this.getFeed = api.getFeed;
|
|
51
|
+
this.getFeeds = api.getFeeds;
|
|
52
|
+
this.hidePost = api.hidePost;
|
|
53
|
+
this.unhidePost = api.unhidePost;
|
|
54
|
+
this.updateAlgorithm = api.updateAlgorithm;
|
|
55
|
+
this.publishAlgorithm = api.publishAlgorithm;
|
|
50
56
|
this.stickyposts = {
|
|
51
57
|
get: api.getStickyPosts,
|
|
52
58
|
create: api.createStickyPost,
|
|
53
59
|
delete: api.deleteStickyPost,
|
|
54
|
-
update: api.updateStickyPost
|
|
55
|
-
}
|
|
60
|
+
update: api.updateStickyPost,
|
|
61
|
+
};
|
|
56
62
|
}
|
|
57
63
|
}
|
|
58
64
|
|
|
59
|
-
const setup = Effect.gen(function*() {
|
|
60
|
-
const client = yield* GrazeClient
|
|
61
|
-
const getFeed = (...args: Parameters<typeof client.getFeed>) =>
|
|
62
|
-
|
|
63
|
-
const
|
|
65
|
+
const setup = Effect.gen(function* () {
|
|
66
|
+
const client = yield* GrazeClient;
|
|
67
|
+
const getFeed = (...args: Parameters<typeof client.getFeed>) =>
|
|
68
|
+
client.getFeed(...args).pipe(Effect.runPromise);
|
|
69
|
+
const getFeeds = (...args: Parameters<typeof client.getFeeds>) =>
|
|
70
|
+
client.getFeeds(...args).pipe(Effect.runPromise);
|
|
71
|
+
const hidePost = (...args: Parameters<typeof client.hidePost>) =>
|
|
72
|
+
client.hidePost(...args).pipe(Effect.runPromise);
|
|
64
73
|
const unhidePost = (...args: Parameters<typeof client.unhidePost>) =>
|
|
65
|
-
client.unhidePost(...args).pipe(Effect.runPromise)
|
|
74
|
+
client.unhidePost(...args).pipe(Effect.runPromise);
|
|
66
75
|
const updateAlgorithm = (
|
|
67
76
|
...args: Parameters<typeof client.updateAlgorithm>
|
|
68
|
-
) => client.updateAlgorithm(...args).pipe(Effect.runPromise)
|
|
77
|
+
) => client.updateAlgorithm(...args).pipe(Effect.runPromise);
|
|
69
78
|
const publishAlgorithm = (
|
|
70
79
|
...args: Parameters<typeof client.publishAlgorithm>
|
|
71
|
-
) => client.publishAlgorithm(...args).pipe(Effect.runPromise)
|
|
80
|
+
) => client.publishAlgorithm(...args).pipe(Effect.runPromise);
|
|
72
81
|
const getStickyPosts = (...args: Parameters<typeof client.stickyposts.get>) =>
|
|
73
|
-
client.stickyposts.get(...args).pipe(Effect.runPromise)
|
|
82
|
+
client.stickyposts.get(...args).pipe(Effect.runPromise);
|
|
74
83
|
const createStickyPost = (
|
|
75
84
|
...args: Parameters<typeof client.stickyposts.create>
|
|
76
|
-
) => client.stickyposts.create(...args).pipe(Effect.runPromise)
|
|
85
|
+
) => client.stickyposts.create(...args).pipe(Effect.runPromise);
|
|
77
86
|
const deleteStickyPost = (
|
|
78
87
|
...args: Parameters<typeof client.stickyposts.delete>
|
|
79
|
-
) => client.stickyposts.delete(...args).pipe(Effect.runPromise)
|
|
88
|
+
) => client.stickyposts.delete(...args).pipe(Effect.runPromise);
|
|
80
89
|
const updateStickyPost = (
|
|
81
90
|
...args: Parameters<typeof client.stickyposts.update>
|
|
82
|
-
) => client.stickyposts.update(...args).pipe(Effect.runPromise)
|
|
91
|
+
) => client.stickyposts.update(...args).pipe(Effect.runPromise);
|
|
83
92
|
|
|
84
93
|
return {
|
|
85
94
|
getFeed,
|
|
@@ -91,9 +100,9 @@ const setup = Effect.gen(function*() {
|
|
|
91
100
|
getStickyPosts,
|
|
92
101
|
createStickyPost,
|
|
93
102
|
deleteStickyPost,
|
|
94
|
-
updateStickyPost
|
|
95
|
-
}
|
|
96
|
-
})
|
|
103
|
+
updateStickyPost,
|
|
104
|
+
};
|
|
105
|
+
});
|
|
97
106
|
|
|
98
|
-
export { Algo, F } from "./builder.js"
|
|
99
|
-
export * from "./schema/index.js"
|
|
107
|
+
export { Algo, F } from "./builder.js";
|
|
108
|
+
export * from "./schema/index.js";
|
package/src/schema/Post.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Schema as S } from "effect"
|
|
1
|
+
import { Schema as S } from "effect";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Schema for Bluesky objects
|
|
@@ -12,24 +12,24 @@ export const Author = S.Struct({
|
|
|
12
12
|
labels: S.Array(S.Any),
|
|
13
13
|
viewer: S.Any,
|
|
14
14
|
createdAt: S.String,
|
|
15
|
-
associated: S.Union(S.Null, S.Any),
|
|
16
|
-
displayName: S.String
|
|
17
|
-
})
|
|
18
|
-
export type Author = typeof Author.Type
|
|
15
|
+
associated: S.Union([S.Null, S.Any]),
|
|
16
|
+
displayName: S.String,
|
|
17
|
+
});
|
|
18
|
+
export type Author = typeof Author.Type;
|
|
19
19
|
|
|
20
20
|
export const Record = S.Struct({
|
|
21
|
-
tags: S.Union(S.Null, S.Any),
|
|
21
|
+
tags: S.Union([S.Null, S.Any]),
|
|
22
22
|
text: S.String,
|
|
23
23
|
$type: S.String,
|
|
24
24
|
embed: S.Any,
|
|
25
25
|
langs: S.NullOr(S.Array(S.String)),
|
|
26
|
-
reply: S.Union(S.Null, S.Any),
|
|
26
|
+
reply: S.Union([S.Null, S.Any]),
|
|
27
27
|
facets: S.NullOr(S.Array(S.Any)),
|
|
28
|
-
labels: S.Union(S.Null, S.Any),
|
|
29
|
-
entities: S.Union(S.Null, S.Any),
|
|
30
|
-
createdAt: S.String
|
|
31
|
-
})
|
|
32
|
-
export type Record = typeof Record.Type
|
|
28
|
+
labels: S.Union([S.Null, S.Any]),
|
|
29
|
+
entities: S.Union([S.Null, S.Any]),
|
|
30
|
+
createdAt: S.String,
|
|
31
|
+
});
|
|
32
|
+
export type Record = typeof Record.Type;
|
|
33
33
|
|
|
34
34
|
export const Post = S.Struct({
|
|
35
35
|
cid: S.String,
|
|
@@ -44,7 +44,7 @@ export const Post = S.Struct({
|
|
|
44
44
|
likeCount: S.Number,
|
|
45
45
|
quoteCount: S.Number,
|
|
46
46
|
replyCount: S.Number,
|
|
47
|
-
threadgate: S.Union(S.Null, S.Unknown),
|
|
48
|
-
repostCount: S.Number
|
|
49
|
-
})
|
|
50
|
-
export type Post = typeof Post.Type
|
|
47
|
+
threadgate: S.Union([S.Null, S.Unknown]),
|
|
48
|
+
repostCount: S.Number,
|
|
49
|
+
});
|
|
50
|
+
export type Post = typeof Post.Type;
|