@laioutr/app-hygraph 1.7.2 → 1.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/dist/module.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@laioutr/app-hygraph",
3
- "version": "1.7.2",
3
+ "version": "1.9.0",
4
4
  "configKey": "@laioutr/app-hygraph",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.1",
package/dist/module.mjs CHANGED
@@ -3,7 +3,7 @@ import { defu } from 'defu';
3
3
  import { registerLaioutrApp } from '@laioutr-core/kit';
4
4
 
5
5
  const name = "@laioutr/app-hygraph";
6
- const version = "1.7.2";
6
+ const version = "1.9.0";
7
7
 
8
8
  const module = defineNuxtModule({
9
9
  meta: {
@@ -1,3 +1,8 @@
1
1
  export declare const blogCollectionToken: any;
2
- export declare const blogTopicToken: any;
3
2
  export declare const blogPostsToken: any;
3
+ /**
4
+ * Ids that the `blog/collection/posts` link should resolve via the topic
5
+ * relation (`topics_some`) instead of the blog-collection relation. Set by the
6
+ * "Blog Posts by Topic" query, which exposes a topic as a BlogCollection entity.
7
+ */
8
+ export declare const blogPostsTopicSourceToken: any;
@@ -1,4 +1,4 @@
1
1
  import { createPassthroughToken } from "#imports";
2
2
  export const blogCollectionToken = createPassthroughToken("hygraph/blog-collection");
3
- export const blogTopicToken = createPassthroughToken("hygraph/blog-topic");
4
3
  export const blogPostsToken = createPassthroughToken("hygraph/blog-posts");
4
+ export const blogPostsTopicSourceToken = createPassthroughToken("hygraph/blog-posts-topic-source");
@@ -12617,6 +12617,8 @@ export type BlogTopicBySlugQuery = {
12617
12617
  };
12618
12618
  export type BlogTopicsQueryVariables = Exact<{
12619
12619
  locales: Array<Locale> | Locale;
12620
+ first?: InputMaybe<Scalars['Int']['input']>;
12621
+ where?: InputMaybe<TopicWhereInput>;
12620
12622
  }>;
12621
12623
  export type BlogTopicsQuery = {
12622
12624
  topics: Array<{
@@ -1,20 +1,30 @@
1
1
  import { BlogCollectionPostsLink } from "@laioutr-core/canonical-types/blog";
2
- import { blogPostsToken } from "../../const/passthroughTokens.js";
2
+ import { blogPostsToken, blogPostsTopicSourceToken } from "../../const/passthroughTokens.js";
3
3
  import { resolveHygraphLocales } from "../../hygraph-utils/locale.js";
4
4
  import { defineHygraph } from "../../middleware/defineHygraph.js";
5
- import { BLOGS_QUERY } from "../../queries/blog.js";
5
+ import { BLOGS_BY_TOPIC_QUERY, BLOGS_QUERY } from "../../queries/blog.js";
6
6
  export default defineHygraph.linkHandler({
7
7
  implements: BlogCollectionPostsLink,
8
8
  run: async ({ context, entityIds, pagination, passthrough, clientEnv }) => {
9
9
  const locales = resolveHygraphLocales(clientEnv.locale);
10
+ const topicSourceIds = passthrough.get(blogPostsTopicSourceToken);
10
11
  const results = await Promise.all(
11
12
  entityIds.map(
12
- (id) => context.hygraph.request(BLOGS_QUERY, {
13
- skip: pagination.offset,
14
- first: pagination.limit,
15
- collectionId: id,
16
- locales
17
- })
13
+ (id) => (
14
+ // Ids exposed by the "Blog Posts by Topic" query are topic ids and must
15
+ // be filtered via the topic relation; all others are blog-collection ids.
16
+ topicSourceIds?.has(id) ? context.hygraph.request(BLOGS_BY_TOPIC_QUERY, {
17
+ skip: pagination.offset,
18
+ first: pagination.limit,
19
+ topicId: id,
20
+ locales
21
+ }) : context.hygraph.request(BLOGS_QUERY, {
22
+ skip: pagination.offset,
23
+ first: pagination.limit,
24
+ collectionId: id,
25
+ locales
26
+ })
27
+ )
18
28
  )
19
29
  );
20
30
  const allPosts = results.flatMap((r) => r.data.blogs);
@@ -1,10 +1,10 @@
1
- import { BlogTopicBySlug, BlogTopicBySlugNotFoundError } from "../../../shared/tokens/blog-topic.js";
2
- import { blogTopicToken } from "../../const/passthroughTokens.js";
1
+ import { BlogPostsByTopic } from "../../../shared/tokens/blog-topic.js";
2
+ import { blogCollectionToken, blogPostsTopicSourceToken } from "../../const/passthroughTokens.js";
3
3
  import { resolveHygraphLocales } from "../../hygraph-utils/locale.js";
4
4
  import { defineHygraph } from "../../middleware/defineHygraph.js";
5
5
  import { BLOG_TOPIC_BY_SLUG_QUERY } from "../../queries/blog.js";
6
6
  export default defineHygraph.queryHandler({
7
- implements: BlogTopicBySlug,
7
+ implements: BlogPostsByTopic,
8
8
  run: async ({ context, input, passthrough, clientEnv }) => {
9
9
  const result = await context.hygraph.request(BLOG_TOPIC_BY_SLUG_QUERY, {
10
10
  slug: input.slug,
@@ -12,9 +12,14 @@ export default defineHygraph.queryHandler({
12
12
  });
13
13
  const topic = result.data.topic;
14
14
  if (!topic) {
15
- throw new BlogTopicBySlugNotFoundError(input.slug);
15
+ return {};
16
16
  }
17
- passthrough.set(blogTopicToken, topic);
17
+ passthrough.set(blogCollectionToken, {
18
+ id: topic.id,
19
+ slug: topic.slug ?? input.slug,
20
+ title: topic.title ?? topic.slug ?? input.slug
21
+ });
22
+ passthrough.set(blogPostsTopicSourceToken, /* @__PURE__ */ new Set([topic.id]));
18
23
  return {
19
24
  id: topic.id
20
25
  };
@@ -0,0 +1,21 @@
1
+ import { BlogPostsByTopic } from "../../../shared/tokens/blog-topic.js";
2
+ import { resolveHygraphLocales } from "../../hygraph-utils/locale.js";
3
+ import { defineHygraph } from "../../middleware/defineHygraph.js";
4
+ import { BLOG_TOPICS_QUERY } from "../../queries/blog.js";
5
+ export default defineHygraph.queryTemplateProvider({
6
+ for: BlogPostsByTopic,
7
+ run: async ({ context, clientEnv, input }) => {
8
+ const term = input.term?.trim();
9
+ const result = await context.hygraph.request(BLOG_TOPICS_QUERY, {
10
+ locales: resolveHygraphLocales(clientEnv.locale),
11
+ // Hygraph caps a page at 100 items; load the full first page instead of
12
+ // the default 10 so every topic is available in the picker.
13
+ first: 100,
14
+ where: term ? { _search: term } : void 0
15
+ });
16
+ return result.data.topics.filter((topic) => Boolean(topic.slug && topic.title)).map((topic) => ({
17
+ input: { slug: topic.slug },
18
+ label: topic.title
19
+ }));
20
+ }
21
+ });
@@ -4,4 +4,4 @@ export declare const BLOG_POST_BY_SLUG_QUERY = "\n #graphql\n \n #graphql\n
4
4
  export declare const BLOG_COLLECTION_BY_SLUG_QUERY = "\n #graphql\n query BlogCollectionBySlug($slug: String!, $locales: [Locale!]!) {\n blogCollection(where: { slug: $slug }, locales: $locales) {\n id\n slug\n title\n }\n }\n";
5
5
  export declare const BLOG_COLLECTIONS_QUERY = "\n #graphql\n query BlogCollections($locales: [Locale!]!) {\n blogCollections(locales: $locales) {\n slug\n title\n }\n }\n";
6
6
  export declare const BLOG_TOPIC_BY_SLUG_QUERY = "\n #graphql\n query BlogTopicBySlug($slug: String!, $locales: [Locale!]!) {\n topic(where: { slug: $slug }, locales: $locales) {\n id\n slug\n title\n }\n }\n";
7
- export declare const BLOG_TOPICS_QUERY = "\n #graphql\n query BlogTopics($locales: [Locale!]!) {\n topics(locales: $locales) {\n slug\n title\n }\n }\n";
7
+ export declare const BLOG_TOPICS_QUERY = "\n #graphql\n query BlogTopics($locales: [Locale!]!, $first: Int, $where: TopicWhereInput) {\n topics(locales: $locales, first: $first, where: $where) {\n slug\n title\n }\n }\n";
@@ -120,8 +120,8 @@ export const BLOG_TOPICS_QUERY = (
120
120
  /* GraphQL */
121
121
  `
122
122
  #graphql
123
- query BlogTopics($locales: [Locale!]!) {
124
- topics(locales: $locales) {
123
+ query BlogTopics($locales: [Locale!]!, $first: Int, $where: TopicWhereInput) {
124
+ topics(locales: $locales, first: $first, where: $where) {
125
125
  slug
126
126
  title
127
127
  }
@@ -1,22 +1,16 @@
1
1
  import z from 'zod/v4';
2
2
  /**
3
- * Local stand-ins for the blog-topic tokens that do not exist in
4
- * `@laioutr-core/canonical-types` yet (neither `entity/blog-topic` nor the
5
- * topic tokens on `/blog`). These mirror the published blog-collection
6
- * equivalents one-to-one, so they can be lifted into the package unchanged
7
- * once it grows a topic surface.
3
+ * Exposes a blog topic AS a `BlogCollection` entity, so it appears in the
4
+ * existing BlogCollection-typed Studio query fields (e.g. "Blog Post Collection
5
+ * With Posts") and reuses the `blog/collection/posts` link no `BlogTopic`
6
+ * entity and no ui-app changes required.
7
+ *
8
+ * The handler resolves the topic by slug and fills the blog-collection
9
+ * passthrough with the topic; the shared posts link then filters by the topic
10
+ * relation for this id (see `blogPostsTopicSourceToken`).
8
11
  */
9
- export declare const BlogTopicBase: import("@laioutr-core/core-types/orchestr").EntityComponentToken<{
10
- name: "base";
11
- } & {
12
- readonly entityType: "BlogTopic";
13
- readonly schema: z.ZodObject<{
14
- slug: z.ZodString;
15
- title: z.ZodString;
16
- }, z.core.$strip>;
17
- }>;
18
- export declare const BlogTopicBySlug: import("@laioutr-core/core-types/orchestr").QueryToken<{
19
- name: "blog/topic/by-slug";
12
+ export declare const BlogPostsByTopic: import("@laioutr-core/core-types/orchestr").QueryToken<{
13
+ name: "blog/posts-by-topic";
20
14
  } & {
21
15
  entity: string;
22
16
  type: "single";
@@ -25,18 +19,3 @@ export declare const BlogTopicBySlug: import("@laioutr-core/core-types/orchestr"
25
19
  slug: z.ZodString;
26
20
  }, z.core.$strip>;
27
21
  }>;
28
- export declare class BlogTopicBySlugNotFoundError extends Error {
29
- static code: string;
30
- readonly code: string;
31
- readonly slug: string;
32
- constructor(slug: string);
33
- }
34
- export declare const BlogTopicPostsLink: import("@laioutr-core/core-types/orchestr").LinkToken<{
35
- name: "blog/topic/posts";
36
- } & {
37
- readonly label: "Blog topic posts";
38
- readonly source: "BlogTopic";
39
- readonly target: "BlogPost";
40
- readonly type: "multi";
41
- readonly defaultLimit: 12;
42
- }>;
@@ -1,32 +1,8 @@
1
1
  import z from "zod/v4";
2
- import { defineEntityComponentToken, defineLinkToken, defineQueryToken } from "@laioutr-core/core-types/orchestr";
3
- export const BlogTopicBase = defineEntityComponentToken("base", {
4
- entityType: "BlogTopic",
5
- schema: z.object({
6
- slug: z.string(),
7
- title: z.string()
8
- })
9
- });
10
- export const BlogTopicBySlug = defineQueryToken("blog/topic/by-slug", {
11
- entity: "BlogTopic",
2
+ import { defineQueryToken } from "@laioutr-core/core-types/orchestr";
3
+ export const BlogPostsByTopic = defineQueryToken("blog/posts-by-topic", {
4
+ entity: "BlogCollection",
12
5
  type: "single",
13
- label: "Blog topic by slug",
6
+ label: "Blog Posts by Topic",
14
7
  input: z.object({ slug: z.string() })
15
8
  });
16
- export class BlogTopicBySlugNotFoundError extends Error {
17
- static code = "BLOG_TOPIC_NOT_FOUND";
18
- code = BlogTopicBySlugNotFoundError.code;
19
- slug;
20
- constructor(slug) {
21
- super(`Blog Topic with slug ${slug} not found`);
22
- this.name = "BlogTopicBySlugNotFoundError";
23
- this.slug = slug;
24
- }
25
- }
26
- export const BlogTopicPostsLink = defineLinkToken("blog/topic/posts", {
27
- label: "Blog topic posts",
28
- source: "BlogTopic",
29
- target: "BlogPost",
30
- type: "multi",
31
- defaultLimit: 12
32
- });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@laioutr/app-hygraph",
3
- "version": "1.7.2",
3
+ "version": "1.9.0",
4
4
  "description": "Laioutr Hygraph App",
5
5
  "repository": "your-org/@laioutr/app-hygraph",
6
6
  "license": "MIT",
@@ -43,6 +43,17 @@
43
43
  "files": [
44
44
  "dist"
45
45
  ],
46
+ "scripts": {
47
+ "dev": "npm run dev:prepare && nuxi dev playground",
48
+ "dev:build": "nuxi build playground",
49
+ "dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground",
50
+ "lint": "eslint .",
51
+ "prepack": "nuxt-module-build build",
52
+ "release": "npm run lint && npm run test && npm run prepack && changelogen --release && npm publish --access public && git push --follow-tags",
53
+ "test": "vitest run",
54
+ "test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit",
55
+ "test:watch": "vitest watch"
56
+ },
46
57
  "dependencies": {
47
58
  "@nuxt/kit": "3.16.2",
48
59
  "zod": "3.25.61"
@@ -92,15 +103,5 @@
92
103
  "engines": {
93
104
  "node": ">=22.12.0",
94
105
  "pnpm": ">=10.15.0"
95
- },
96
- "scripts": {
97
- "dev": "npm run dev:prepare && nuxi dev playground",
98
- "dev:build": "nuxi build playground",
99
- "dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground",
100
- "lint": "eslint .",
101
- "release": "npm run lint && npm run test && npm run prepack && changelogen --release && npm publish --access public && git push --follow-tags",
102
- "test": "vitest run",
103
- "test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit",
104
- "test:watch": "vitest watch"
105
106
  }
106
- }
107
+ }
@@ -1,22 +0,0 @@
1
- import { BlogTopicBase } from "../../../shared/tokens/blog-topic.js";
2
- import { blogTopicToken } from "../../const/passthroughTokens.js";
3
- import { defineHygraph } from "../../middleware/defineHygraph.js";
4
- export default defineHygraph.componentResolver({
5
- entityType: "BlogTopic",
6
- label: "Blog Topic",
7
- provides: [BlogTopicBase],
8
- resolve: async ({ passthrough, $entity }) => {
9
- const topic = passthrough.require(blogTopicToken);
10
- return {
11
- entities: [
12
- $entity({
13
- id: topic.id,
14
- base: {
15
- slug: topic.slug,
16
- title: topic.title
17
- }
18
- })
19
- ]
20
- };
21
- }
22
- });
@@ -1,2 +0,0 @@
1
- declare const _default: import("nitropack").NitroAppPlugin;
2
- export default _default;
@@ -1,16 +0,0 @@
1
- import { BlogTopicBySlug } from "../../../shared/tokens/blog-topic.js";
2
- import { resolveHygraphLocales } from "../../hygraph-utils/locale.js";
3
- import { defineHygraph } from "../../middleware/defineHygraph.js";
4
- import { BLOG_TOPICS_QUERY } from "../../queries/blog.js";
5
- export default defineHygraph.queryTemplateProvider({
6
- for: BlogTopicBySlug,
7
- run: async ({ context, clientEnv }) => {
8
- const result = await context.hygraph.request(BLOG_TOPICS_QUERY, {
9
- locales: resolveHygraphLocales(clientEnv.locale)
10
- });
11
- return result.data.topics.map((topic) => ({
12
- input: { slug: topic.slug },
13
- label: topic.title
14
- }));
15
- }
16
- });
@@ -1,2 +0,0 @@
1
- declare const _default: import("nitropack").NitroAppPlugin;
2
- export default _default;
@@ -1,33 +0,0 @@
1
- import { BlogTopicPostsLink } from "../../../shared/tokens/blog-topic.js";
2
- import { blogPostsToken } from "../../const/passthroughTokens.js";
3
- import { resolveHygraphLocales } from "../../hygraph-utils/locale.js";
4
- import { defineHygraph } from "../../middleware/defineHygraph.js";
5
- import { BLOGS_BY_TOPIC_QUERY } from "../../queries/blog.js";
6
- export default defineHygraph.linkHandler({
7
- implements: BlogTopicPostsLink,
8
- run: async ({ context, entityIds, pagination, passthrough, clientEnv }) => {
9
- const locales = resolveHygraphLocales(clientEnv.locale);
10
- const results = await Promise.all(
11
- entityIds.map(
12
- (id) => context.hygraph.request(BLOGS_BY_TOPIC_QUERY, {
13
- skip: pagination.offset,
14
- first: pagination.limit,
15
- topicId: id,
16
- locales
17
- })
18
- )
19
- );
20
- const allPosts = results.flatMap((r) => r.data.blogs);
21
- passthrough.set(blogPostsToken, allPosts);
22
- return {
23
- links: entityIds.map((id, i) => {
24
- const data = results.at(i)?.data;
25
- return {
26
- sourceId: id,
27
- targetIds: data?.blogs.map((blog) => blog.id) ?? [],
28
- entityTotal: data?.blogsConnection.aggregate.count ?? data?.blogs.length ?? 0
29
- };
30
- })
31
- };
32
- }
33
- });