graze-client 0.8.0 → 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.
Files changed (45) hide show
  1. package/build/dts/GrazeClient.d.ts +368 -242
  2. package/build/dts/GrazeClient.d.ts.map +1 -1
  3. package/build/dts/builder.d.ts +8 -8
  4. package/build/dts/builder.d.ts.map +1 -1
  5. package/build/dts/common.d.ts +2 -3
  6. package/build/dts/common.d.ts.map +1 -1
  7. package/build/dts/index.d.ts +13 -13
  8. package/build/dts/index.d.ts.map +1 -1
  9. package/build/dts/schema/Post.d.ts +52 -52
  10. package/build/dts/schema/StickyPosts.d.ts +55 -56
  11. package/build/dts/schema/StickyPosts.d.ts.map +1 -1
  12. package/build/dts/schema/UpdateAlgorithmPayload.d.ts +187 -163
  13. package/build/dts/schema/UpdateAlgorithmPayload.d.ts.map +1 -1
  14. package/build/dts/schema/shared.d.ts +127 -127
  15. package/build/dts/schema/shared.d.ts.map +1 -1
  16. package/build/dts/stickyposts.d.ts +80 -78
  17. package/build/dts/stickyposts.d.ts.map +1 -1
  18. package/build/esm/GrazeClient.js +45 -29
  19. package/build/esm/GrazeClient.js.map +1 -1
  20. package/build/esm/builder.js +2 -2
  21. package/build/esm/builder.js.map +1 -1
  22. package/build/esm/common.js +4 -3
  23. package/build/esm/common.js.map +1 -1
  24. package/build/esm/index.js +8 -5
  25. package/build/esm/index.js.map +1 -1
  26. package/build/esm/schema/Post.js +6 -6
  27. package/build/esm/schema/Post.js.map +1 -1
  28. package/build/esm/schema/StickyPosts.js +2 -3
  29. package/build/esm/schema/StickyPosts.js.map +1 -1
  30. package/build/esm/schema/UpdateAlgorithmPayload.js +116 -39
  31. package/build/esm/schema/UpdateAlgorithmPayload.js.map +1 -1
  32. package/build/esm/schema/shared.js +6 -6
  33. package/build/esm/schema/shared.js.map +1 -1
  34. package/build/esm/stickyposts.js +28 -13
  35. package/build/esm/stickyposts.js.map +1 -1
  36. package/package.json +8 -10
  37. package/src/GrazeClient.ts +55 -47
  38. package/src/builder.ts +14 -14
  39. package/src/common.ts +4 -9
  40. package/src/index.ts +24 -19
  41. package/src/schema/Post.ts +6 -6
  42. package/src/schema/StickyPosts.ts +3 -7
  43. package/src/schema/UpdateAlgorithmPayload.ts +80 -67
  44. package/src/schema/shared.ts +6 -6
  45. package/src/stickyposts.ts +44 -23
@@ -1,3 +1,4 @@
1
+ import { Config, Context, Data, Effect, Layer, Redacted, Schema } from "effect";
1
2
  import {
2
3
  AlgoUpdateResponse,
3
4
  GetAlgoResponse,
@@ -5,21 +6,18 @@ import {
5
6
  HidePostBody,
6
7
  UnhidePostBody,
7
8
  } from "./schema/shared.js";
8
- import {
9
- FetchHttpClient,
10
- HttpApi,
11
- HttpApiClient,
12
- HttpApiEndpoint,
13
- HttpApiGroup,
14
- HttpApiSchema,
15
- HttpClient,
16
- HttpClientRequest,
17
- } from "@effect/platform";
18
- import { Config, Data, Effect, Redacted, Schema } from "effect";
19
9
 
20
- import * as Form from "./schema/UpdateAlgorithmPayload.js";
21
10
  import { FeedIdParam, UserIdParam } from "./common.js";
11
+ import * as Form from "./schema/UpdateAlgorithmPayload.js";
22
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
23
  return typeof val === "string" || val instanceof Blob;
@@ -27,10 +25,10 @@ function isStringOrBlob(val: unknown): val is string | Blob {
27
25
 
28
26
  const encodeForm = (obj: Form.FullForm) =>
29
27
  Effect.gen(function* () {
30
- const encoded = yield* Schema.encode(Form.FullForm)(obj);
28
+ const encoded = yield* Schema.encodeEffect(Form.FullForm)(obj);
31
29
  const formData = new FormData();
32
30
  for (const key in encoded) {
33
- if (encoded.hasOwnProperty(key)) {
31
+ if (Object.prototype.hasOwnProperty.call(encoded, key)) {
34
32
  const val = encoded[key as keyof Form.FullForm];
35
33
  if (isStringOrBlob(val)) {
36
34
  formData.append(key, val);
@@ -42,44 +40,49 @@ const encodeForm = (obj: Form.FullForm) =>
42
40
 
43
41
  const GrazeApiGroup = HttpApiGroup.make("graze", { topLevel: true })
44
42
  .add(
45
- HttpApiEndpoint.get("getFeeds")`/app/my_feeds`
46
- .addSuccess(GetAlgorithmsResponse)
47
- .setPayload(
48
- Schema.Struct({
49
- user_id: UserIdParam,
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")`/app/my_feeds/${FeedIdParam}`.addSuccess(
55
- GetAlgoResponse
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")`/app/edit_algo_image`
60
- .setPayload(HttpApiSchema.Multipart(Form.FullForm))
61
- .addSuccess(AlgoUpdateResponse)
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
- "publishAlgorithm"
66
- )`/app/publish_algo/${FeedIdParam}`.addSuccess(Schema.Unknown)
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")`/app/hide_post`
70
- .setPayload(HidePostBody)
71
- .addSuccess(Schema.Unknown)
71
+ HttpApiEndpoint.post("hidePost", "/app/hide_post", {
72
+ payload: HidePostBody,
73
+ success: Schema.Unknown,
74
+ }),
72
75
  )
73
76
  .add(
74
- HttpApiEndpoint.post("unhidePost")`/app/unhide_post`
75
- .setPayload(UnhidePostBody)
76
- .addSuccess(Schema.Unknown)
77
+ HttpApiEndpoint.post("unhidePost", "/app/unhide_post", {
78
+ payload: UnhidePostBody,
79
+ success: Schema.Unknown,
80
+ }),
77
81
  );
78
82
 
79
83
  class GrazeApiDef extends HttpApi.make("grazeApi")
80
84
  .add(GrazeApiGroup)
81
- .add(StickyPostsApiGroup)
82
- .addError(Schema.Unknown) {}
85
+ .add(StickyPostsApiGroup) {}
83
86
 
84
87
  const grazeLive = Effect.gen(function* () {
85
88
  const baseUrl = yield* Config.url("GRAZE_API_URL");
@@ -93,8 +96,8 @@ const grazeLive = Effect.gen(function* () {
93
96
  HttpClient.mapRequest(
94
97
  HttpClientRequest.setHeaders({
95
98
  cookie: Redacted.value(cookie),
96
- })
97
- )
99
+ }),
100
+ ),
98
101
  ),
99
102
  });
100
103
 
@@ -104,8 +107,8 @@ const grazeLive = Effect.gen(function* () {
104
107
  return yield* client.updateAlgorithm({ payload: formData });
105
108
  });
106
109
 
107
- const userId = yield* Config.integer("GRAZE_USER_ID");
108
- const getFeeds = () => client.getFeeds({ payload: { user_id: userId } });
110
+ const userId = yield* Config.int("GRAZE_USER_ID");
111
+ const getFeeds = () => client.getFeeds({ query: { user_id: userId } });
109
112
 
110
113
  return {
111
114
  ...client,
@@ -114,13 +117,18 @@ const grazeLive = Effect.gen(function* () {
114
117
  };
115
118
  });
116
119
 
117
- export class GrazeClient extends Effect.Service<GrazeClient>()("GrazeClient", {
118
- effect: grazeLive,
119
- dependencies: [FetchHttpClient.layer],
120
- }) {
121
- // TODO: test service
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
+ );
122
127
  }
123
128
 
129
+ /** The resolved service shape (v4 has no `.Service` namespace on the tag). */
130
+ export type GrazeClientService = Effect.Success<typeof grazeLive>;
131
+
124
132
  export class GrazeError extends Data.TaggedError("GrazeError")<{
125
133
  message: string;
126
134
  status: number;
package/src/builder.ts CHANGED
@@ -1,4 +1,4 @@
1
- import {
1
+ import type {
2
2
  AttributeComparison,
3
3
  AttributeName,
4
4
  AttributeValue,
@@ -18,8 +18,8 @@ const neverMatch: Filter = {
18
18
  or: [],
19
19
  };
20
20
 
21
- const and = (...filters: (Filter | null)[]): Filter => {
22
- const flatFilters: Filter[] = [];
21
+ const and = (...filters: Array<Filter | null>): 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.
@@ -33,8 +33,8 @@ const and = (...filters: (Filter | null)[]): Filter => {
33
33
  });
34
34
  return { and: flatFilters };
35
35
  };
36
- const or = (...filters: (Filter | null)[]): Filter => {
37
- const flatFilters: Filter[] = [];
36
+ const or = (...filters: Array<Filter | null>): Filter => {
37
+ const flatFilters: Array<Filter> = [];
38
38
  filters
39
39
  .filter((f) => f !== null)
40
40
  .forEach((f) => {
@@ -49,26 +49,26 @@ const or = (...filters: (Filter | null)[]): Filter => {
49
49
 
50
50
  const regexMatches = (
51
51
  attribute: RegexMatches["regex_matches"][0],
52
- regex: string
52
+ regex: string,
53
53
  ): RegexMatches => ({
54
54
  regex_matches: [attribute, regex, true],
55
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
61
  regex_negation_matches: [attribute, regex, true],
62
62
  });
63
63
 
64
64
  const textMatchesAny = ({
65
65
  attribute,
66
- patterns,
67
66
  caseInsensitive = true,
67
+ patterns,
68
68
  useRegex = false,
69
69
  }: {
70
70
  attribute: AttributeName;
71
- patterns: string[];
71
+ patterns: Array<string>;
72
72
  caseInsensitive?: boolean;
73
73
  useRegex?: boolean;
74
74
  }): TextMatchesAny => ({
@@ -77,12 +77,12 @@ const textMatchesAny = ({
77
77
 
78
78
  const textMatchesNone = ({
79
79
  attribute,
80
- patterns,
81
80
  caseInsensitive = true,
81
+ patterns,
82
82
  useRegex = false,
83
83
  }: {
84
84
  attribute: AttributeName;
85
- patterns: string[];
85
+ patterns: Array<string>;
86
86
  caseInsensitive?: boolean;
87
87
  useRegex?: boolean;
88
88
  }): TextMatchesNone => ({
@@ -98,7 +98,7 @@ const isNotListMember = (uri: string): ListMemberFilter => ({
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
103
  social_graph: [identifier, op, relation],
104
104
  });
@@ -133,7 +133,7 @@ const hasNoNSFWLabels: Filter = {
133
133
  const attributeCompare = (
134
134
  attr: AttributeName,
135
135
  op: AttributeComparison,
136
- value: AttributeValue
136
+ value: AttributeValue,
137
137
  ): Filter => ({
138
138
  attribute_compare: [attr, op, value],
139
139
  });
@@ -149,7 +149,7 @@ const isContentOk = (threshold: number = 0.9): ContentModerationBlock => ({
149
149
  });
150
150
 
151
151
  export const Algo = {
152
- filter: (...filters: Filter[]): { filter: Filter } => ({
152
+ filter: (...filters: Array<Filter>): { filter: Filter } => ({
153
153
  filter: and(...filters),
154
154
  }),
155
155
  };
package/src/common.ts CHANGED
@@ -1,11 +1,6 @@
1
- import { HttpApiSchema } from "@effect/platform";
2
1
  import { Schema } from "effect";
3
2
 
4
- export const UserIdParam = HttpApiSchema.param(
5
- "userId",
6
- Schema.NumberFromString
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,24 +1,24 @@
1
- import { ConfigProvider, Effect, Layer } from "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
- ...args: any[]
6
- ) => Effect.Effect<infer A, infer E, never>
5
+ ...args: Array<any>
6
+ ) => Effect.Effect<infer A, infer _E, never>
7
7
  ? (...args: Parameters<T>) => Promise<A>
8
8
  : never;
9
9
 
10
10
  export class GrazeService {
11
- getFeed: Promisify<typeof GrazeClient.Service.getFeed>;
12
- getFeeds: Promisify<typeof GrazeClient.Service.getFeeds>;
13
- hidePost: Promisify<typeof GrazeClient.Service.hidePost>;
14
- unhidePost: Promisify<typeof GrazeClient.Service.unhidePost>;
15
- updateAlgorithm: Promisify<typeof GrazeClient.Service.updateAlgorithm>;
16
- publishAlgorithm: Promisify<typeof GrazeClient.Service.publishAlgorithm>;
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"]>;
17
17
  stickyposts: {
18
- get: Promisify<typeof GrazeClient.Service.stickyposts.get>;
19
- create: Promisify<typeof GrazeClient.Service.stickyposts.create>;
20
- delete: Promisify<typeof GrazeClient.Service.stickyposts.delete>;
21
- update: Promisify<typeof GrazeClient.Service.stickyposts.update>;
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
24
  constructor({
@@ -35,11 +35,16 @@ export class GrazeService {
35
35
  ["GRAZE_COOKIE", cookie],
36
36
  ["GRAZE_USER_ID", userId.toString()],
37
37
  ]);
38
- const provider = ConfigProvider.fromMap(envMap);
39
- const layer = Layer.setConfigProvider(provider);
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);
40
45
  const make = setup.pipe(
41
- Effect.provide(GrazeClient.Default),
42
- Effect.provide(layer)
46
+ Effect.provide(GrazeClient.layer),
47
+ Effect.provide(layer),
43
48
  );
44
49
  const api = Effect.runSync(make);
45
50
  this.getFeed = api.getFeed;
@@ -99,5 +104,5 @@ const setup = Effect.gen(function* () {
99
104
  };
100
105
  });
101
106
 
107
+ export { Algo, F } from "./builder.js";
102
108
  export * from "./schema/index.js";
103
- export { F, Algo } from "./builder.js";
@@ -12,21 +12,21 @@ 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),
15
+ associated: S.Union([S.Null, S.Any]),
16
16
  displayName: S.String,
17
17
  });
18
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),
28
+ labels: S.Union([S.Null, S.Any]),
29
+ entities: S.Union([S.Null, S.Any]),
30
30
  createdAt: S.String,
31
31
  });
32
32
  export type Record = typeof Record.Type;
@@ -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),
47
+ threadgate: S.Union([S.Null, S.Unknown]),
48
48
  repostCount: S.Number,
49
49
  });
50
50
  export type Post = typeof Post.Type;
@@ -1,11 +1,7 @@
1
1
  import { Schema as S } from "effect";
2
2
  import { Post } from "./Post.js";
3
- import { HttpApiSchema } from "@effect/platform";
4
3
 
5
- export const StickyPostIdParam = HttpApiSchema.param(
6
- "stickyPostId",
7
- S.NumberFromString
8
- );
4
+ export const StickyPostIdParam = S.NumberFromString;
9
5
  export type StickyPostIdParam = typeof StickyPostIdParam.Type;
10
6
 
11
7
  export const StoppingCriteria = S.Struct({
@@ -13,7 +9,7 @@ export const StoppingCriteria = S.Struct({
13
9
  });
14
10
  export type StoppingCriteria = typeof StoppingCriteria.Type;
15
11
 
16
- export const StickyType = S.Literal("pinned", "rotating");
12
+ export const StickyType = S.Literals(["pinned", "rotating"]);
17
13
  export type StickyType = typeof StickyType.Type;
18
14
 
19
15
  const StickyPostBase = S.Struct({
@@ -41,7 +37,7 @@ export const GetStickyPostsBody = S.Struct({
41
37
  author: S.String,
42
38
  created_at: S.String,
43
39
  updated_at: S.String,
44
- })
40
+ }),
45
41
  ),
46
42
  });
47
43
  export type GetStickyPostsBody = typeof GetStickyPostsBody.Type;