@laioutr/app-hygraph 1.9.0 → 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/generated/graphql.d.ts +33 -1
- package/dist/runtime/server/generated/graphql.js +3 -0
- package/dist/runtime/server/media-library/hygraph.js +2 -1
- package/dist/runtime/server/middleware/defineHygraph.js +3 -1
- package/dist/runtime/server/orchestr/blog/blogCollection.resolver.js +10 -2
- 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 +15 -16
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
|
};
|
|
@@ -5720,6 +5720,7 @@ export type Glossary = Entity & Node & {
|
|
|
5720
5720
|
/** Get the other localizations for this document */
|
|
5721
5721
|
localizations: Array<Glossary>;
|
|
5722
5722
|
metaDescription?: Maybe<Scalars['String']['output']>;
|
|
5723
|
+
metaKeywords?: Maybe<Scalars['String']['output']>;
|
|
5723
5724
|
metaTitle?: Maybe<Scalars['String']['output']>;
|
|
5724
5725
|
/** The time the document was published. Null on documents in draft stage. */
|
|
5725
5726
|
publishedAt?: Maybe<Scalars['DateTime']['output']>;
|
|
@@ -5822,6 +5823,8 @@ export type GlossaryCreateInput = {
|
|
|
5822
5823
|
localizations?: InputMaybe<GlossaryCreateLocalizationsInput>;
|
|
5823
5824
|
/** metaDescription input for default locale (en) */
|
|
5824
5825
|
metaDescription?: InputMaybe<Scalars['String']['input']>;
|
|
5826
|
+
/** metaKeywords input for default locale (en) */
|
|
5827
|
+
metaKeywords?: InputMaybe<Scalars['String']['input']>;
|
|
5825
5828
|
/** metaTitle input for default locale (en) */
|
|
5826
5829
|
metaTitle?: InputMaybe<Scalars['String']['input']>;
|
|
5827
5830
|
slug: Scalars['String']['input'];
|
|
@@ -5833,6 +5836,7 @@ export type GlossaryCreateLocalizationDataInput = {
|
|
|
5833
5836
|
content: Scalars['RichTextAST']['input'];
|
|
5834
5837
|
createdAt?: InputMaybe<Scalars['DateTime']['input']>;
|
|
5835
5838
|
metaDescription?: InputMaybe<Scalars['String']['input']>;
|
|
5839
|
+
metaKeywords?: InputMaybe<Scalars['String']['input']>;
|
|
5836
5840
|
metaTitle?: InputMaybe<Scalars['String']['input']>;
|
|
5837
5841
|
title: Scalars['String']['input'];
|
|
5838
5842
|
updatedAt?: InputMaybe<Scalars['DateTime']['input']>;
|
|
@@ -5975,6 +5979,8 @@ export declare enum GlossaryOrderByInput {
|
|
|
5975
5979
|
IdDesc = "id_DESC",
|
|
5976
5980
|
MetaDescriptionAsc = "metaDescription_ASC",
|
|
5977
5981
|
MetaDescriptionDesc = "metaDescription_DESC",
|
|
5982
|
+
MetaKeywordsAsc = "metaKeywords_ASC",
|
|
5983
|
+
MetaKeywordsDesc = "metaKeywords_DESC",
|
|
5978
5984
|
MetaTitleAsc = "metaTitle_ASC",
|
|
5979
5985
|
MetaTitleDesc = "metaTitle_DESC",
|
|
5980
5986
|
PublishedAtAsc = "publishedAt_ASC",
|
|
@@ -5993,6 +5999,8 @@ export type GlossaryUpdateInput = {
|
|
|
5993
5999
|
localizations?: InputMaybe<GlossaryUpdateLocalizationsInput>;
|
|
5994
6000
|
/** metaDescription input for default locale (en) */
|
|
5995
6001
|
metaDescription?: InputMaybe<Scalars['String']['input']>;
|
|
6002
|
+
/** metaKeywords input for default locale (en) */
|
|
6003
|
+
metaKeywords?: InputMaybe<Scalars['String']['input']>;
|
|
5996
6004
|
/** metaTitle input for default locale (en) */
|
|
5997
6005
|
metaTitle?: InputMaybe<Scalars['String']['input']>;
|
|
5998
6006
|
slug?: InputMaybe<Scalars['String']['input']>;
|
|
@@ -6002,6 +6010,7 @@ export type GlossaryUpdateInput = {
|
|
|
6002
6010
|
export type GlossaryUpdateLocalizationDataInput = {
|
|
6003
6011
|
content?: InputMaybe<Scalars['RichTextAST']['input']>;
|
|
6004
6012
|
metaDescription?: InputMaybe<Scalars['String']['input']>;
|
|
6013
|
+
metaKeywords?: InputMaybe<Scalars['String']['input']>;
|
|
6005
6014
|
metaTitle?: InputMaybe<Scalars['String']['input']>;
|
|
6006
6015
|
title?: InputMaybe<Scalars['String']['input']>;
|
|
6007
6016
|
};
|
|
@@ -6041,6 +6050,8 @@ export type GlossaryUpdateManyInput = {
|
|
|
6041
6050
|
localizations?: InputMaybe<GlossaryUpdateManyLocalizationsInput>;
|
|
6042
6051
|
/** metaDescription input for default locale (en) */
|
|
6043
6052
|
metaDescription?: InputMaybe<Scalars['String']['input']>;
|
|
6053
|
+
/** metaKeywords input for default locale (en) */
|
|
6054
|
+
metaKeywords?: InputMaybe<Scalars['String']['input']>;
|
|
6044
6055
|
/** metaTitle input for default locale (en) */
|
|
6045
6056
|
metaTitle?: InputMaybe<Scalars['String']['input']>;
|
|
6046
6057
|
/** title input for default locale (en) */
|
|
@@ -6049,6 +6060,7 @@ export type GlossaryUpdateManyInput = {
|
|
|
6049
6060
|
export type GlossaryUpdateManyLocalizationDataInput = {
|
|
6050
6061
|
content?: InputMaybe<Scalars['RichTextAST']['input']>;
|
|
6051
6062
|
metaDescription?: InputMaybe<Scalars['String']['input']>;
|
|
6063
|
+
metaKeywords?: InputMaybe<Scalars['String']['input']>;
|
|
6052
6064
|
metaTitle?: InputMaybe<Scalars['String']['input']>;
|
|
6053
6065
|
title?: InputMaybe<Scalars['String']['input']>;
|
|
6054
6066
|
};
|
|
@@ -6175,6 +6187,25 @@ export type GlossaryWhereInput = {
|
|
|
6175
6187
|
metaDescription_not_starts_with?: InputMaybe<Scalars['String']['input']>;
|
|
6176
6188
|
/** All values starting with the given string. */
|
|
6177
6189
|
metaDescription_starts_with?: InputMaybe<Scalars['String']['input']>;
|
|
6190
|
+
metaKeywords?: InputMaybe<Scalars['String']['input']>;
|
|
6191
|
+
/** All values containing the given string. */
|
|
6192
|
+
metaKeywords_contains?: InputMaybe<Scalars['String']['input']>;
|
|
6193
|
+
/** All values ending with the given string. */
|
|
6194
|
+
metaKeywords_ends_with?: InputMaybe<Scalars['String']['input']>;
|
|
6195
|
+
/** All values that are contained in given list. */
|
|
6196
|
+
metaKeywords_in?: InputMaybe<Array<InputMaybe<Scalars['String']['input']>>>;
|
|
6197
|
+
/** Any other value that exists and is not equal to the given value. */
|
|
6198
|
+
metaKeywords_not?: InputMaybe<Scalars['String']['input']>;
|
|
6199
|
+
/** All values not containing the given string. */
|
|
6200
|
+
metaKeywords_not_contains?: InputMaybe<Scalars['String']['input']>;
|
|
6201
|
+
/** All values not ending with the given string */
|
|
6202
|
+
metaKeywords_not_ends_with?: InputMaybe<Scalars['String']['input']>;
|
|
6203
|
+
/** All values that are not contained in given list. */
|
|
6204
|
+
metaKeywords_not_in?: InputMaybe<Array<InputMaybe<Scalars['String']['input']>>>;
|
|
6205
|
+
/** All values not starting with the given string. */
|
|
6206
|
+
metaKeywords_not_starts_with?: InputMaybe<Scalars['String']['input']>;
|
|
6207
|
+
/** All values starting with the given string. */
|
|
6208
|
+
metaKeywords_starts_with?: InputMaybe<Scalars['String']['input']>;
|
|
6178
6209
|
metaTitle?: InputMaybe<Scalars['String']['input']>;
|
|
6179
6210
|
/** All values containing the given string. */
|
|
6180
6211
|
metaTitle_contains?: InputMaybe<Scalars['String']['input']>;
|
|
@@ -7621,6 +7652,7 @@ export declare enum Locale {
|
|
|
7621
7652
|
De = "de",
|
|
7622
7653
|
/** System locale */
|
|
7623
7654
|
En = "en",
|
|
7655
|
+
Es = "es",
|
|
7624
7656
|
Fr = "fr"
|
|
7625
7657
|
}
|
|
7626
7658
|
/** Representing a geolocation point with latitude and longitude */
|
|
@@ -12629,7 +12661,7 @@ export type BlogTopicsQuery = {
|
|
|
12629
12661
|
export type MediaLibraryListQueryVariables = Exact<{
|
|
12630
12662
|
skip?: InputMaybe<Scalars['Int']['input']>;
|
|
12631
12663
|
first?: InputMaybe<Scalars['Int']['input']>;
|
|
12632
|
-
|
|
12664
|
+
where?: InputMaybe<AssetWhereInput>;
|
|
12633
12665
|
}>;
|
|
12634
12666
|
export type MediaLibraryListQuery = {
|
|
12635
12667
|
assets: Array<{
|
|
@@ -215,6 +215,8 @@ export var GlossaryOrderByInput = /* @__PURE__ */ ((GlossaryOrderByInput2) => {
|
|
|
215
215
|
GlossaryOrderByInput2["IdDesc"] = "id_DESC";
|
|
216
216
|
GlossaryOrderByInput2["MetaDescriptionAsc"] = "metaDescription_ASC";
|
|
217
217
|
GlossaryOrderByInput2["MetaDescriptionDesc"] = "metaDescription_DESC";
|
|
218
|
+
GlossaryOrderByInput2["MetaKeywordsAsc"] = "metaKeywords_ASC";
|
|
219
|
+
GlossaryOrderByInput2["MetaKeywordsDesc"] = "metaKeywords_DESC";
|
|
218
220
|
GlossaryOrderByInput2["MetaTitleAsc"] = "metaTitle_ASC";
|
|
219
221
|
GlossaryOrderByInput2["MetaTitleDesc"] = "metaTitle_DESC";
|
|
220
222
|
GlossaryOrderByInput2["PublishedAtAsc"] = "publishedAt_ASC";
|
|
@@ -275,6 +277,7 @@ export var LearningResourceOrderByInput = /* @__PURE__ */ ((LearningResourceOrde
|
|
|
275
277
|
export var Locale = /* @__PURE__ */ ((Locale2) => {
|
|
276
278
|
Locale2["De"] = "de";
|
|
277
279
|
Locale2["En"] = "en";
|
|
280
|
+
Locale2["Es"] = "es";
|
|
278
281
|
Locale2["Fr"] = "fr";
|
|
279
282
|
return Locale2;
|
|
280
283
|
})(Locale || {});
|
|
@@ -11,7 +11,8 @@ export default defineMediaLibraryProvider({
|
|
|
11
11
|
const result = await client.request(MEDIA_LIBRARY_LIST_QUERY, {
|
|
12
12
|
skip: offset,
|
|
13
13
|
first: limit,
|
|
14
|
-
|
|
14
|
+
// Only constrain by `_search` when a term is present; an empty filter lists all assets (browse).
|
|
15
|
+
where: search ? { _search: search } : {}
|
|
15
16
|
});
|
|
16
17
|
const items = result.data.assets.map(
|
|
17
18
|
(asset) => ({
|
|
@@ -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,10 +1,10 @@
|
|
|
1
|
-
import { BlogCollectionBase } from "@laioutr-core/canonical-types/entity/blog-collection";
|
|
1
|
+
import { BlogCollectionBase, BlogCollectionSeo } from "@laioutr-core/canonical-types/entity/blog-collection";
|
|
2
2
|
import { blogCollectionToken } from "../../const/passthroughTokens.js";
|
|
3
3
|
import { defineHygraph } from "../../middleware/defineHygraph.js";
|
|
4
4
|
export default defineHygraph.componentResolver({
|
|
5
5
|
entityType: "BlogCollection",
|
|
6
6
|
label: "Blog Collection",
|
|
7
|
-
provides: [BlogCollectionBase],
|
|
7
|
+
provides: [BlogCollectionBase, BlogCollectionSeo],
|
|
8
8
|
resolve: async ({ passthrough, $entity }) => {
|
|
9
9
|
const collection = passthrough.require(blogCollectionToken);
|
|
10
10
|
return {
|
|
@@ -14,6 +14,14 @@ export default defineHygraph.componentResolver({
|
|
|
14
14
|
base: {
|
|
15
15
|
slug: collection.slug,
|
|
16
16
|
title: collection.title
|
|
17
|
+
},
|
|
18
|
+
// Resolve `seo` locally so BlogCollection seo requests stay in the
|
|
19
|
+
// Hygraph app. Without a Hygraph provider, the shared `BlogCollection`
|
|
20
|
+
// entity type routes seo to another app's resolver (e.g. Shopify's
|
|
21
|
+
// `nodes(ids:)` query), which rejects the Hygraph id as an invalid
|
|
22
|
+
// global id. Hygraph blog collections/topics only carry `title`.
|
|
23
|
+
seo: {
|
|
24
|
+
title: collection.title
|
|
17
25
|
}
|
|
18
26
|
})
|
|
19
27
|
]
|
|
@@ -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, $
|
|
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, $
|
|
8
|
-
assets(skip: $skip, first: $first, orderBy: createdAt_DESC, 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;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@laioutr/app-hygraph",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"description": "Laioutr Hygraph App",
|
|
5
5
|
"repository": "your-org/@laioutr/app-hygraph",
|
|
6
6
|
"license": "MIT",
|
|
@@ -43,17 +43,6 @@
|
|
|
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
|
-
},
|
|
57
46
|
"dependencies": {
|
|
58
47
|
"@nuxt/kit": "3.16.2",
|
|
59
48
|
"zod": "3.25.61"
|
|
@@ -87,9 +76,9 @@
|
|
|
87
76
|
"peerDependencies": {
|
|
88
77
|
"@graphql-codegen/cli": "^6.0.0",
|
|
89
78
|
"@laioutr-core/canonical-types": ">=0.25.0",
|
|
90
|
-
"@laioutr-core/core-types": ">=0.
|
|
91
|
-
"@laioutr-core/frontend-core": ">=0.
|
|
92
|
-
"@laioutr-core/kit": ">=0.
|
|
79
|
+
"@laioutr-core/core-types": ">=0.35.0",
|
|
80
|
+
"@laioutr-core/frontend-core": ">=0.35.0",
|
|
81
|
+
"@laioutr-core/kit": ">=0.35.0",
|
|
93
82
|
"graphql": "^16.0.0"
|
|
94
83
|
},
|
|
95
84
|
"peerDependenciesMeta": {
|
|
@@ -103,5 +92,15 @@
|
|
|
103
92
|
"engines": {
|
|
104
93
|
"node": ">=22.12.0",
|
|
105
94
|
"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"
|
|
106
105
|
}
|
|
107
|
-
}
|
|
106
|
+
}
|