@laioutr/app-hygraph 1.9.1 → 1.10.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/README.md +7 -0
- package/dist/module.d.mts +8 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +1 -1
- package/dist/runtime/server/client/hygraph.d.ts +3 -1
- package/dist/runtime/server/client/hygraph.js +16 -4
- package/dist/runtime/server/middleware/defineHygraph.js +3 -1
- package/dist/runtime/server/queries/blog.d.ts +7 -7
- package/dist/runtime/server/queries/blog.js +16 -14
- package/dist/runtime/server/queries/mediaLibrary.d.ts +1 -1
- package/dist/runtime/server/queries/mediaLibrary.js +2 -2
- package/dist/runtime/server/types/hygraph.d.ts +8 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,6 +24,10 @@ Other Laioutr apps that also talk to Hygraph can import the client, middleware,
|
|
|
24
24
|
|
|
25
25
|
The module also includes a demo blog implementation with orchestr-handlers for blog content: query handlers and query template providers for blog collections and posts, link handlers for connecting posts to collections, and component resolvers for rendering both.
|
|
26
26
|
|
|
27
|
+
### Content Preview
|
|
28
|
+
|
|
29
|
+
When a request is marked as a preview (`clientEnv.isPreview`, set by Laioutr's content-preview gate), the connector reads Hygraph's **Draft** stage instead of **Published**, so editors see unpublished changes. This requires a [`previewToken`](#configuration) with Draft-stage permission; without one the connector fails soft and keeps serving published content.
|
|
30
|
+
|
|
27
31
|
## Configuration
|
|
28
32
|
|
|
29
33
|
Add the module to your `nuxt.config.ts` and provide your Hygraph credentials:
|
|
@@ -44,6 +48,8 @@ export default defineNuxtConfig({
|
|
|
44
48
|
| `contentApiUrl` | Hygraph Content API endpoint |
|
|
45
49
|
| `imageBaseUrl` | Hygraph image CDN base URL (used by [`@nuxt/image`](https://image.nuxt.com/providers/hygraph)). Region-specific; find yours by querying `{ assets(first: 1) { url } }` against your Content API and taking the domain + first path-segment, e.g. `https://eu-west-2.graphassets.com/cmh4jxx7w1fce07l80dils2d1` |
|
|
46
50
|
| `token` | Permanent auth token for the Content API |
|
|
51
|
+
| `previewToken` | Optional. Separate PAT with Draft-stage read permission, used only for preview requests. Kept distinct from `token` so published reads keep a least-privileged credential. Without it the connector always serves published content, even for requests marked as preview. |
|
|
52
|
+
| `previewApiUrl` | Optional. Alternate Content API endpoint for preview (Draft) reads. Hygraph's Content API already serves both stages, so this is only needed if a project observes CDN staleness on drafts. |
|
|
47
53
|
|
|
48
54
|
## Exports
|
|
49
55
|
|
|
@@ -132,6 +138,7 @@ npx @laioutr/cli rc fetch -p <org-slug>/<project-slug> -s <secret-key>
|
|
|
132
138
|
export HYGRAPH_CONTENT_API_URL=<your-content-api-url>
|
|
133
139
|
export HYGRAPH_TOKEN=<your-token>
|
|
134
140
|
export HYGRAPH_IMAGE_BASE_URL=<your-image-base-url>
|
|
141
|
+
export HYGRAPH_PREVIEW_TOKEN=<your-draft-stage-token> # optional, enables preview/draft reads
|
|
135
142
|
pnpm dev
|
|
136
143
|
```
|
|
137
144
|
|
package/dist/module.d.mts
CHANGED
|
@@ -7,6 +7,14 @@ interface ModuleOptions {
|
|
|
7
7
|
contentApiUrl: string;
|
|
8
8
|
imageBaseUrl: string;
|
|
9
9
|
token: string;
|
|
10
|
+
/**
|
|
11
|
+
* Optional. PAT with Draft-stage read permission. Separate from `token` so published reads keep a
|
|
12
|
+
* least-privileged credential. Without it the connector always serves published content, even when
|
|
13
|
+
* a verified preview token marks the request as a preview.
|
|
14
|
+
*/
|
|
15
|
+
previewToken?: string;
|
|
16
|
+
/** Optional. Hygraph's Content API serves both stages; only needed if a project observes CDN staleness on drafts. */
|
|
17
|
+
previewApiUrl?: string;
|
|
10
18
|
}
|
|
11
19
|
/**
|
|
12
20
|
* The config the module adds to nuxt.runtimeConfig.public['@laioutr/app-hygraph']
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { HygraphClientConfig, HygraphResponse } from '../types/hygraph.js';
|
|
2
2
|
export type { HygraphResponse };
|
|
3
|
-
export declare const hygraphClientFactory: (explicitConfig?: HygraphClientConfig
|
|
3
|
+
export declare const hygraphClientFactory: (explicitConfig?: HygraphClientConfig, options?: {
|
|
4
|
+
isPreview?: boolean;
|
|
5
|
+
}) => {
|
|
4
6
|
request: <TData = unknown>(query: string, variables?: unknown) => Promise<HygraphResponse<TData>>;
|
|
5
7
|
};
|
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
import { useRuntimeConfig } from "#imports";
|
|
2
|
-
|
|
2
|
+
import { Stage } from "../generated/graphql.js";
|
|
3
|
+
export const hygraphClientFactory = (explicitConfig, options) => {
|
|
3
4
|
const config = explicitConfig ?? useRuntimeConfig()["@laioutr/app-hygraph"];
|
|
5
|
+
let isPreview = options?.isPreview ?? false;
|
|
6
|
+
const previewToken = config.previewToken?.trim();
|
|
7
|
+
if (isPreview && !previewToken) {
|
|
8
|
+
console.warn("[app-hygraph] preview requested but previewToken is not configured \u2014 serving published content.");
|
|
9
|
+
isPreview = false;
|
|
10
|
+
}
|
|
4
11
|
const request = async (query, variables = null) => {
|
|
5
|
-
const res = await fetch(config.contentApiUrl, {
|
|
12
|
+
const res = await fetch(isPreview ? config.previewApiUrl ?? config.contentApiUrl : config.contentApiUrl, {
|
|
6
13
|
method: "POST",
|
|
7
14
|
headers: {
|
|
8
|
-
Authorization: `Bearer ${config.token}`
|
|
15
|
+
Authorization: `Bearer ${isPreview ? previewToken : config.token}`
|
|
9
16
|
},
|
|
10
|
-
|
|
17
|
+
// Every document in `queries/` declares `$stage: Stage = PUBLISHED`, so the variable can be
|
|
18
|
+
// injected unconditionally without the client knowing which operation it is sending.
|
|
19
|
+
body: JSON.stringify({
|
|
20
|
+
query,
|
|
21
|
+
variables: { ...variables, stage: isPreview ? Stage.Draft : Stage.Published }
|
|
22
|
+
})
|
|
11
23
|
});
|
|
12
24
|
return res.json();
|
|
13
25
|
};
|
|
@@ -4,4 +4,6 @@ export const defineHygraph = defineOrchestr.meta({
|
|
|
4
4
|
app: "@laioutr/app-hygraph",
|
|
5
5
|
logoUrl: "/app-hygraph/logo.svg",
|
|
6
6
|
label: "Hygraph"
|
|
7
|
-
}).extendRequest(async () => ({
|
|
7
|
+
}).extendRequest(async ({ clientEnv }) => ({
|
|
8
|
+
context: { hygraph: hygraphClientFactory(void 0, { isPreview: clientEnv.isPreview }) }
|
|
9
|
+
}));
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export declare const BLOGS_QUERY = "\n #graphql\n \n #graphql\n \n #graphql\n fragment Asset on Asset {\n url\n fileName\n mimeType\n width\n height\n handle\n }\n\n fragment Blog on Blog {\n id\n title\n slug\n content {\n html\n }\n image {\n ...Asset\n }\n publishedAt\n metaTitle\n metaDescription\n }\n\n query Blogs($skip: Int, $first: Int, $collectionId: ID, $locales: [Locale!]
|
|
2
|
-
export declare const BLOGS_BY_TOPIC_QUERY = "\n #graphql\n \n #graphql\n \n #graphql\n fragment Asset on Asset {\n url\n fileName\n mimeType\n width\n height\n handle\n }\n\n fragment Blog on Blog {\n id\n title\n slug\n content {\n html\n }\n image {\n ...Asset\n }\n publishedAt\n metaTitle\n metaDescription\n }\n\n query BlogsByTopic($skip: Int, $first: Int, $topicId: ID, $locales: [Locale!]
|
|
3
|
-
export declare const BLOG_POST_BY_SLUG_QUERY = "\n #graphql\n \n #graphql\n \n #graphql\n fragment Asset on Asset {\n url\n fileName\n mimeType\n width\n height\n handle\n }\n\n fragment Blog on Blog {\n id\n title\n slug\n content {\n html\n }\n image {\n ...Asset\n }\n publishedAt\n metaTitle\n metaDescription\n }\n\n query BlogPostBySlug($slug: String!, $locales: [Locale!]
|
|
4
|
-
export declare const BLOG_COLLECTION_BY_SLUG_QUERY = "\n #graphql\n query BlogCollectionBySlug($slug: String!, $locales: [Locale!]
|
|
5
|
-
export declare const BLOG_COLLECTIONS_QUERY = "\n #graphql\n query BlogCollections($locales: [Locale!]
|
|
6
|
-
export declare const BLOG_TOPIC_BY_SLUG_QUERY = "\n #graphql\n query BlogTopicBySlug($slug: String!, $locales: [Locale!]
|
|
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";
|
|
1
|
+
export declare const BLOGS_QUERY = "\n #graphql\n \n #graphql\n \n #graphql\n fragment Asset on Asset {\n url\n fileName\n mimeType\n width\n height\n handle\n }\n\n fragment Blog on Blog {\n id\n title\n slug\n content {\n html\n }\n image {\n ...Asset\n }\n publishedAt\n metaTitle\n metaDescription\n }\n\n query Blogs($skip: Int, $first: Int, $collectionId: ID, $locales: [Locale!]!, $stage: Stage = PUBLISHED) {\n blogs(\n skip: $skip\n first: $first\n orderBy: createdAt_DESC\n where: { blogCollection: { id: $collectionId } }\n locales: $locales\n stage: $stage\n ) {\n ...Blog\n }\n blogsConnection(where: { blogCollection: { id: $collectionId } }, locales: $locales, stage: $stage) {\n aggregate {\n count\n }\n }\n }\n";
|
|
2
|
+
export declare const BLOGS_BY_TOPIC_QUERY = "\n #graphql\n \n #graphql\n \n #graphql\n fragment Asset on Asset {\n url\n fileName\n mimeType\n width\n height\n handle\n }\n\n fragment Blog on Blog {\n id\n title\n slug\n content {\n html\n }\n image {\n ...Asset\n }\n publishedAt\n metaTitle\n metaDescription\n }\n\n query BlogsByTopic($skip: Int, $first: Int, $topicId: ID, $locales: [Locale!]!, $stage: Stage = PUBLISHED) {\n blogs(\n skip: $skip\n first: $first\n orderBy: createdAt_DESC\n where: { topics_some: { id: $topicId } }\n locales: $locales\n stage: $stage\n ) {\n ...Blog\n }\n blogsConnection(where: { topics_some: { id: $topicId } }, locales: $locales, stage: $stage) {\n aggregate {\n count\n }\n }\n }\n";
|
|
3
|
+
export declare const BLOG_POST_BY_SLUG_QUERY = "\n #graphql\n \n #graphql\n \n #graphql\n fragment Asset on Asset {\n url\n fileName\n mimeType\n width\n height\n handle\n }\n\n fragment Blog on Blog {\n id\n title\n slug\n content {\n html\n }\n image {\n ...Asset\n }\n publishedAt\n metaTitle\n metaDescription\n }\n\n query BlogPostBySlug($slug: String!, $locales: [Locale!]!, $stage: Stage = PUBLISHED) {\n blog(where: { slug: $slug }, locales: $locales, stage: $stage) {\n ...Blog\n }\n }\n";
|
|
4
|
+
export declare const BLOG_COLLECTION_BY_SLUG_QUERY = "\n #graphql\n query BlogCollectionBySlug($slug: String!, $locales: [Locale!]!, $stage: Stage = PUBLISHED) {\n blogCollection(where: { slug: $slug }, locales: $locales, stage: $stage) {\n id\n slug\n title\n }\n }\n";
|
|
5
|
+
export declare const BLOG_COLLECTIONS_QUERY = "\n #graphql\n query BlogCollections($locales: [Locale!]!, $stage: Stage = PUBLISHED) {\n blogCollections(locales: $locales, stage: $stage) {\n slug\n title\n }\n }\n";
|
|
6
|
+
export declare const BLOG_TOPIC_BY_SLUG_QUERY = "\n #graphql\n query BlogTopicBySlug($slug: String!, $locales: [Locale!]!, $stage: Stage = PUBLISHED) {\n topic(where: { slug: $slug }, locales: $locales, stage: $stage) {\n id\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, $stage: Stage = PUBLISHED) {\n topics(locales: $locales, first: $first, where: $where, stage: $stage) {\n slug\n title\n }\n }\n";
|
|
@@ -25,17 +25,18 @@ export const BLOGS_QUERY = (
|
|
|
25
25
|
`
|
|
26
26
|
#graphql
|
|
27
27
|
${BlogFragment}
|
|
28
|
-
query Blogs($skip: Int, $first: Int, $collectionId: ID, $locales: [Locale!]
|
|
28
|
+
query Blogs($skip: Int, $first: Int, $collectionId: ID, $locales: [Locale!]!, $stage: Stage = PUBLISHED) {
|
|
29
29
|
blogs(
|
|
30
30
|
skip: $skip
|
|
31
31
|
first: $first
|
|
32
32
|
orderBy: createdAt_DESC
|
|
33
33
|
where: { blogCollection: { id: $collectionId } }
|
|
34
34
|
locales: $locales
|
|
35
|
+
stage: $stage
|
|
35
36
|
) {
|
|
36
37
|
...Blog
|
|
37
38
|
}
|
|
38
|
-
blogsConnection(where: { blogCollection: { id: $collectionId } }, locales: $locales) {
|
|
39
|
+
blogsConnection(where: { blogCollection: { id: $collectionId } }, locales: $locales, stage: $stage) {
|
|
39
40
|
aggregate {
|
|
40
41
|
count
|
|
41
42
|
}
|
|
@@ -48,17 +49,18 @@ export const BLOGS_BY_TOPIC_QUERY = (
|
|
|
48
49
|
`
|
|
49
50
|
#graphql
|
|
50
51
|
${BlogFragment}
|
|
51
|
-
query BlogsByTopic($skip: Int, $first: Int, $topicId: ID, $locales: [Locale!]
|
|
52
|
+
query BlogsByTopic($skip: Int, $first: Int, $topicId: ID, $locales: [Locale!]!, $stage: Stage = PUBLISHED) {
|
|
52
53
|
blogs(
|
|
53
54
|
skip: $skip
|
|
54
55
|
first: $first
|
|
55
56
|
orderBy: createdAt_DESC
|
|
56
57
|
where: { topics_some: { id: $topicId } }
|
|
57
58
|
locales: $locales
|
|
59
|
+
stage: $stage
|
|
58
60
|
) {
|
|
59
61
|
...Blog
|
|
60
62
|
}
|
|
61
|
-
blogsConnection(where: { topics_some: { id: $topicId } }, locales: $locales) {
|
|
63
|
+
blogsConnection(where: { topics_some: { id: $topicId } }, locales: $locales, stage: $stage) {
|
|
62
64
|
aggregate {
|
|
63
65
|
count
|
|
64
66
|
}
|
|
@@ -71,8 +73,8 @@ export const BLOG_POST_BY_SLUG_QUERY = (
|
|
|
71
73
|
`
|
|
72
74
|
#graphql
|
|
73
75
|
${BlogFragment}
|
|
74
|
-
query BlogPostBySlug($slug: String!, $locales: [Locale!]
|
|
75
|
-
blog(where: { slug: $slug }, locales: $locales) {
|
|
76
|
+
query BlogPostBySlug($slug: String!, $locales: [Locale!]!, $stage: Stage = PUBLISHED) {
|
|
77
|
+
blog(where: { slug: $slug }, locales: $locales, stage: $stage) {
|
|
76
78
|
...Blog
|
|
77
79
|
}
|
|
78
80
|
}
|
|
@@ -82,8 +84,8 @@ export const BLOG_COLLECTION_BY_SLUG_QUERY = (
|
|
|
82
84
|
/* GraphQL */
|
|
83
85
|
`
|
|
84
86
|
#graphql
|
|
85
|
-
query BlogCollectionBySlug($slug: String!, $locales: [Locale!]
|
|
86
|
-
blogCollection(where: { slug: $slug }, locales: $locales) {
|
|
87
|
+
query BlogCollectionBySlug($slug: String!, $locales: [Locale!]!, $stage: Stage = PUBLISHED) {
|
|
88
|
+
blogCollection(where: { slug: $slug }, locales: $locales, stage: $stage) {
|
|
87
89
|
id
|
|
88
90
|
slug
|
|
89
91
|
title
|
|
@@ -95,8 +97,8 @@ export const BLOG_COLLECTIONS_QUERY = (
|
|
|
95
97
|
/* GraphQL */
|
|
96
98
|
`
|
|
97
99
|
#graphql
|
|
98
|
-
query BlogCollections($locales: [Locale!]
|
|
99
|
-
blogCollections(locales: $locales) {
|
|
100
|
+
query BlogCollections($locales: [Locale!]!, $stage: Stage = PUBLISHED) {
|
|
101
|
+
blogCollections(locales: $locales, stage: $stage) {
|
|
100
102
|
slug
|
|
101
103
|
title
|
|
102
104
|
}
|
|
@@ -107,8 +109,8 @@ export const BLOG_TOPIC_BY_SLUG_QUERY = (
|
|
|
107
109
|
/* GraphQL */
|
|
108
110
|
`
|
|
109
111
|
#graphql
|
|
110
|
-
query BlogTopicBySlug($slug: String!, $locales: [Locale!]
|
|
111
|
-
topic(where: { slug: $slug }, locales: $locales) {
|
|
112
|
+
query BlogTopicBySlug($slug: String!, $locales: [Locale!]!, $stage: Stage = PUBLISHED) {
|
|
113
|
+
topic(where: { slug: $slug }, locales: $locales, stage: $stage) {
|
|
112
114
|
id
|
|
113
115
|
slug
|
|
114
116
|
title
|
|
@@ -120,8 +122,8 @@ export const BLOG_TOPICS_QUERY = (
|
|
|
120
122
|
/* GraphQL */
|
|
121
123
|
`
|
|
122
124
|
#graphql
|
|
123
|
-
query BlogTopics($locales: [Locale!]!, $first: Int, $where: TopicWhereInput) {
|
|
124
|
-
topics(locales: $locales, first: $first, where: $where) {
|
|
125
|
+
query BlogTopics($locales: [Locale!]!, $first: Int, $where: TopicWhereInput, $stage: Stage = PUBLISHED) {
|
|
126
|
+
topics(locales: $locales, first: $first, where: $where, stage: $stage) {
|
|
125
127
|
slug
|
|
126
128
|
title
|
|
127
129
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const MEDIA_LIBRARY_LIST_QUERY = "\n #graphql\n \n #graphql\n fragment Asset on Asset {\n url\n fileName\n mimeType\n width\n height\n handle\n }\n\n query MediaLibraryList($skip: Int, $first: Int, $where: AssetWhereInput) {\n assets(skip: $skip, first: $first, orderBy: createdAt_DESC, where: $where) {\n ...Asset\n }\n }\n";
|
|
1
|
+
export declare const MEDIA_LIBRARY_LIST_QUERY = "\n #graphql\n \n #graphql\n fragment Asset on Asset {\n url\n fileName\n mimeType\n width\n height\n handle\n }\n\n query MediaLibraryList($skip: Int, $first: Int, $where: AssetWhereInput, $stage: Stage = PUBLISHED) {\n assets(skip: $skip, first: $first, orderBy: createdAt_DESC, where: $where, stage: $stage) {\n ...Asset\n }\n }\n";
|
|
@@ -4,8 +4,8 @@ export const MEDIA_LIBRARY_LIST_QUERY = (
|
|
|
4
4
|
`
|
|
5
5
|
#graphql
|
|
6
6
|
${AssetFragment}
|
|
7
|
-
query MediaLibraryList($skip: Int, $first: Int, $where: AssetWhereInput) {
|
|
8
|
-
assets(skip: $skip, first: $first, orderBy: createdAt_DESC, where: $where) {
|
|
7
|
+
query MediaLibraryList($skip: Int, $first: Int, $where: AssetWhereInput, $stage: Stage = PUBLISHED) {
|
|
8
|
+
assets(skip: $skip, first: $first, orderBy: createdAt_DESC, where: $where, stage: $stage) {
|
|
9
9
|
...Asset
|
|
10
10
|
}
|
|
11
11
|
}
|
|
@@ -9,6 +9,14 @@ export interface HygraphAsset {
|
|
|
9
9
|
export interface HygraphClientConfig {
|
|
10
10
|
contentApiUrl: string;
|
|
11
11
|
token: string;
|
|
12
|
+
/**
|
|
13
|
+
* Optional. PAT with Draft-stage read permission. Separate from `token` so published reads keep a
|
|
14
|
+
* least-privileged credential. Without it the connector always serves published content, even when
|
|
15
|
+
* a verified preview token marks the request as a preview.
|
|
16
|
+
*/
|
|
17
|
+
previewToken?: string;
|
|
18
|
+
/** Optional. Hygraph's Content API serves both stages; only needed if a project observes CDN staleness on drafts. */
|
|
19
|
+
previewApiUrl?: string;
|
|
12
20
|
}
|
|
13
21
|
export interface HygraphResponse<TData> {
|
|
14
22
|
data: TData;
|