@stonecrop/graphql-client 0.31.0 → 0.33.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.
@@ -1,172 +0,0 @@
1
- import { GET_META_QUERY, GET_ALL_META_QUERY, RUN_ACTION_MUTATION } from './queries';
2
- /**
3
- * Client for interacting with Stonecrop GraphQL API.
4
- *
5
- * Acts as a transport layer for stonecropRecord/stonecropRecords/stonecropAction.
6
- *
7
- * @public
8
- */
9
- export class StonecropClient {
10
- endpoint;
11
- headers;
12
- metaCache = new Map();
13
- constructor(options) {
14
- this.endpoint = options.endpoint;
15
- this.headers = {
16
- 'Content-Type': 'application/json',
17
- ...options.headers,
18
- };
19
- }
20
- /**
21
- * Execute a GraphQL query against the configured endpoint.
22
- *
23
- * @param query - GraphQL query string
24
- * @param variables - Query variables
25
- * @throws Error if the GraphQL response contains errors
26
- */
27
- async query(query, variables) {
28
- const response = await fetch(this.endpoint, {
29
- method: 'POST',
30
- headers: this.headers,
31
- body: JSON.stringify({ query, variables }),
32
- });
33
- const json = await response.json();
34
- if (json.errors?.length) {
35
- throw new Error(json.errors[0].message);
36
- }
37
- if (json.data === undefined) {
38
- throw new Error('GraphQL response missing data field');
39
- }
40
- return json.data;
41
- }
42
- /**
43
- * Execute a GraphQL mutation. Delegates to query() since both use POST.
44
- *
45
- * @param mutation - GraphQL mutation string
46
- * @param variables - Mutation variables
47
- */
48
- async mutate(mutation, variables) {
49
- return this.query(mutation, variables);
50
- }
51
- /**
52
- * Get doctype metadata
53
- * @param context - Doctype context containing doctype name
54
- */
55
- async getMeta(context) {
56
- const cached = this.metaCache.get(context.doctype);
57
- if (cached)
58
- return cached;
59
- const result = await this.query(GET_META_QUERY, {
60
- doctype: context.doctype,
61
- });
62
- if (result.stonecropMeta) {
63
- this.metaCache.set(context.doctype, result.stonecropMeta);
64
- }
65
- return result.stonecropMeta;
66
- }
67
- /**
68
- * Get all doctype metadata
69
- */
70
- async getAllMeta() {
71
- const result = await this.query(GET_ALL_META_QUERY);
72
- for (const meta of result.stonecropAllMeta) {
73
- this.metaCache.set(meta.name, meta);
74
- }
75
- return result.stonecropAllMeta;
76
- }
77
- /**
78
- * Get a single record by ID.
79
- *
80
- * Routes through the stonecropRecord resolver which handles nested data
81
- * fetching based on the includeNested option.
82
- *
83
- * @param doctype - Doctype reference (name and optional slug)
84
- * @param recordId - Record ID to fetch
85
- * @param options - Query options (includeNested, maxDepth)
86
- */
87
- async getRecord(doctype, recordId, options) {
88
- const result = await this.query(`query GetRecord($doctype: String!, $id: String!, $options: JSON) {
89
- stonecropRecord(doctype: $doctype, id: $id, options: $options) {
90
- data
91
- unknownLinks
92
- }
93
- }`, {
94
- doctype: doctype.name,
95
- id: recordId,
96
- options: options?.includeNested
97
- ? {
98
- includeNested: options.includeNested,
99
- maxDepth: options.maxDepth,
100
- }
101
- : undefined,
102
- });
103
- return {
104
- record: result.stonecropRecord?.data ?? null,
105
- unknownLinks: result.stonecropRecord?.unknownLinks,
106
- };
107
- }
108
- /**
109
- * Get multiple records with optional filtering and pagination.
110
- *
111
- * Returns flat arrays — the middleware merges connection format (\{ nodes: [...] \})
112
- * into plain arrays before returning.
113
- *
114
- * @param doctype - Doctype reference (name and optional slug)
115
- * @param options - Query options (filters, orderBy, limit, offset)
116
- */
117
- async getRecords(doctype, options) {
118
- const result = await this.query(`
119
- query GetRecords(
120
- $doctype: String!
121
- $filters: JSON
122
- $orderBy: String
123
- $limit: Int
124
- $offset: Int
125
- $includeTotal: Boolean
126
- ) {
127
- stonecropRecords(
128
- doctype: $doctype
129
- filters: $filters
130
- orderBy: $orderBy
131
- limit: $limit
132
- offset: $offset
133
- includeTotal: $includeTotal
134
- ) {
135
- data
136
- hasMore
137
- count
138
- }
139
- }
140
- `, {
141
- doctype: doctype.name,
142
- ...options,
143
- });
144
- const { data, hasMore, count } = result.stonecropRecords;
145
- // `count` is null unless includeTotal was set. Omitting the key rather than passing null
146
- // through keeps "not asked for" and "asked for, and it is zero" distinguishable.
147
- return count == null ? { data, hasMore } : { data, hasMore, count };
148
- }
149
- /**
150
- * Execute a doctype action
151
- * @param doctype - Doctype reference (name and optional slug)
152
- * @param action - Action name to execute
153
- * @param args - Action arguments
154
- */
155
- async runAction(doctype, action, args) {
156
- const result = await this.query(RUN_ACTION_MUTATION, {
157
- doctype: doctype.name,
158
- action,
159
- args,
160
- });
161
- return result.stonecropAction;
162
- }
163
- /**
164
- * Clear the cached doctype metadata.
165
- *
166
- * Call this if the server-side doctype schema has changed and you need
167
- * to fetch fresh metadata (e.g., after adding a new field).
168
- */
169
- clearMetaCache() {
170
- this.metaCache.clear();
171
- }
172
- }
@@ -1,4 +0,0 @@
1
- export type { DoctypeMeta } from '@stonecrop/schema';
2
- export { StonecropClient, type StonecropClientOptions, type DoctypeContext } from './client';
3
- export type { GetRecordResult } from './types';
4
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAEpD,OAAO,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,KAAK,cAAc,EAAE,MAAM,UAAU,CAAA;AAC5F,YAAY,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA"}
package/dist/src/index.js DELETED
@@ -1 +0,0 @@
1
- export { StonecropClient } from './client';
@@ -1,23 +0,0 @@
1
- /**
2
- * GraphQL query documents sent by {@link StonecropClient} to the middleware.
3
- *
4
- * These are the client's half of the wire contract with `@stonecrop/graphql-middleware`.
5
- * They live here as exported constants (rather than inline in the client methods) so the
6
- * cross-package contract test can validate the exact strings the client sends against the
7
- * middleware's published SDL — a field the server drops while a query still selects it must
8
- * fail CI, not production.
9
- *
10
- * @public
11
- */
12
- export declare const GET_META_QUERY = "\n\tquery GetMeta($doctype: String!) {\n\t\tstonecropMeta(doctype: $doctype) {\n\t\t\tname\n\t\t\tslug\n\t\t\tdisplayField\n\t\t\tfields {\n\t\t\t\tkind\n\t\t\t\tfieldname\n\t\t\t\tcomponent\n\t\t\t\tprimaryKey\n\t\t\t\tcomputed\n\t\t\t\tlanguage\n\t\t\t\tdoctype\n\t\t\t\tlabel\n\t\t\t\twidth\n\t\t\t\theight\n\t\t\t\talign\n\t\t\t\tedit\n\t\t\t\tmask\n\t\t\t\tformat\n\t\t\t\tmode\n\t\t\t\toptions\n\t\t\t\trequired\n\t\t\t\treadOnly\n\t\t\t\thidden\n\t\t\t\tdefault\n\t\t\t\tvalidation\n\t\t\t\tcardinality\n\t\t\t\tsource\n\t\t\t}\n\t\t\tworkflow {\n\t\t\t\tstates\n\t\t\t\tactions {\n\t\t\t\t\tlabel\n\t\t\t\t\trequiredFields\n\t\t\t\t\tallowedStates\n\t\t\t\t\tnextState\n\t\t\t\t\tstateless\n\t\t\t\t\tselfTransition\n\t\t\t\t\tclientHandler\n\t\t\t\t}\n\t\t\t}\n\t\t\tinherits\n\t\t}\n\t}\n";
13
- /**
14
- * Mutation document for dispatching a workflow action (the server-owned transition).
15
- * @public
16
- */
17
- export declare const RUN_ACTION_MUTATION = "\n\tmutation RunAction($doctype: String!, $action: String!, $args: JSON) {\n\t\tstonecropAction(doctype: $doctype, action: $action, args: $args) {\n\t\t\tsuccess\n\t\t\tdata\n\t\t\terror\n\t\t}\n\t}\n";
18
- /**
19
- * Query document for fetching all doctype metadata.
20
- * @public
21
- */
22
- export declare const GET_ALL_META_QUERY = "\n\tquery GetAllMeta {\n\t\tstonecropAllMeta {\n\t\t\tname\n\t\t\tslug\n\t\t\tdisplayField\n\t\t\tfields {\n\t\t\t\tkind\n\t\t\t\tfieldname\n\t\t\t\tcomponent\n\t\t\t\tprimaryKey\n\t\t\t\tcomputed\n\t\t\t\tlanguage\n\t\t\t\tdoctype\n\t\t\t\tlabel\n\t\t\t\twidth\n\t\t\t\theight\n\t\t\t\talign\n\t\t\t\tedit\n\t\t\t\tmask\n\t\t\t\tformat\n\t\t\t\tmode\n\t\t\t\toptions\n\t\t\t\trequired\n\t\t\t\treadOnly\n\t\t\t\thidden\n\t\t\t\tdefault\n\t\t\t\tvalidation\n\t\t\t\tcardinality\n\t\t\t\tsource\n\t\t\t}\n\t\t\tworkflow {\n\t\t\t\tstates\n\t\t\t\tactions {\n\t\t\t\t\tlabel\n\t\t\t\t\trequiredFields\n\t\t\t\t\tallowedStates\n\t\t\t\t\tnextState\n\t\t\t\t\tstateless\n\t\t\t\t\tselfTransition\n\t\t\t\t\tclientHandler\n\t\t\t\t}\n\t\t\t}\n\t\t\tinherits\n\t\t}\n\t}\n";
23
- //# sourceMappingURL=queries.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../../src/queries.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,eAAO,MAAM,cAAc,myBA8C1B,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,mBAAmB,6MAQ/B,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,kBAAkB,mwBA8C9B,CAAA"}
@@ -1,122 +0,0 @@
1
- /**
2
- * GraphQL query documents sent by {@link StonecropClient} to the middleware.
3
- *
4
- * These are the client's half of the wire contract with `@stonecrop/graphql-middleware`.
5
- * They live here as exported constants (rather than inline in the client methods) so the
6
- * cross-package contract test can validate the exact strings the client sends against the
7
- * middleware's published SDL — a field the server drops while a query still selects it must
8
- * fail CI, not production.
9
- *
10
- * @public
11
- */
12
- export const GET_META_QUERY = `
13
- query GetMeta($doctype: String!) {
14
- stonecropMeta(doctype: $doctype) {
15
- name
16
- slug
17
- displayField
18
- fields {
19
- kind
20
- fieldname
21
- component
22
- primaryKey
23
- computed
24
- language
25
- doctype
26
- label
27
- width
28
- height
29
- align
30
- edit
31
- mask
32
- format
33
- mode
34
- options
35
- required
36
- readOnly
37
- hidden
38
- default
39
- validation
40
- cardinality
41
- source
42
- }
43
- workflow {
44
- states
45
- actions {
46
- label
47
- requiredFields
48
- allowedStates
49
- nextState
50
- stateless
51
- selfTransition
52
- clientHandler
53
- }
54
- }
55
- inherits
56
- }
57
- }
58
- `;
59
- /**
60
- * Mutation document for dispatching a workflow action (the server-owned transition).
61
- * @public
62
- */
63
- export const RUN_ACTION_MUTATION = `
64
- mutation RunAction($doctype: String!, $action: String!, $args: JSON) {
65
- stonecropAction(doctype: $doctype, action: $action, args: $args) {
66
- success
67
- data
68
- error
69
- }
70
- }
71
- `;
72
- /**
73
- * Query document for fetching all doctype metadata.
74
- * @public
75
- */
76
- export const GET_ALL_META_QUERY = `
77
- query GetAllMeta {
78
- stonecropAllMeta {
79
- name
80
- slug
81
- displayField
82
- fields {
83
- kind
84
- fieldname
85
- component
86
- primaryKey
87
- computed
88
- language
89
- doctype
90
- label
91
- width
92
- height
93
- align
94
- edit
95
- mask
96
- format
97
- mode
98
- options
99
- required
100
- readOnly
101
- hidden
102
- default
103
- validation
104
- cardinality
105
- source
106
- }
107
- workflow {
108
- states
109
- actions {
110
- label
111
- requiredFields
112
- allowedStates
113
- nextState
114
- stateless
115
- selfTransition
116
- clientHandler
117
- }
118
- }
119
- inherits
120
- }
121
- }
122
- `;
@@ -1,14 +0,0 @@
1
- /**
2
- * @file Types for the GraphQL client.
3
- * @public
4
- */
5
- import type { GetRecordResult as SchemaGetRecordResult } from '@stonecrop/schema';
6
- /**
7
- * Result from getRecord - includes the record data and any unknown links requested
8
- * @public
9
- */
10
- export interface GetRecordResult extends SchemaGetRecordResult {
11
- /** Link names that were requested but don't exist in the doctype schema */
12
- unknownLinks?: string[];
13
- }
14
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/types/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,eAAe,IAAI,qBAAqB,EAAE,MAAM,mBAAmB,CAAA;AAEjF;;;GAGG;AACH,MAAM,WAAW,eAAgB,SAAQ,qBAAqB;IAC7D,2EAA2E;IAC3E,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;CACvB"}
@@ -1,4 +0,0 @@
1
- /**
2
- * @file Types for the GraphQL client.
3
- * @public
4
- */
package/src/client.ts DELETED
@@ -1,233 +0,0 @@
1
- import type {
2
- DataClient,
3
- DoctypeMeta,
4
- DoctypeContext,
5
- DoctypeRef,
6
- GetRecordOptions,
7
- GetRecordsOptions,
8
- GetRecordsResult,
9
- } from '@stonecrop/schema'
10
- import type { GetRecordResult } from './types'
11
- import { GET_META_QUERY, GET_ALL_META_QUERY, RUN_ACTION_MUTATION } from './queries'
12
-
13
- export type { DoctypeContext, DoctypeRef }
14
- export type { GetRecordResult, GetRecordsResult }
15
-
16
- /**
17
- * Options for creating a Stonecrop client
18
- * @public
19
- */
20
- export interface StonecropClientOptions {
21
- /** GraphQL endpoint URL */
22
- endpoint: string
23
- /** Additional HTTP headers to include in requests */
24
- headers?: Record<string, string>
25
- }
26
-
27
- /**
28
- * Client for interacting with Stonecrop GraphQL API.
29
- *
30
- * Acts as a transport layer for stonecropRecord/stonecropRecords/stonecropAction.
31
- *
32
- * @public
33
- */
34
- export class StonecropClient implements DataClient {
35
- private endpoint: string
36
- private headers: Record<string, string>
37
- private metaCache: Map<string, DoctypeMeta> = new Map()
38
-
39
- constructor(options: StonecropClientOptions) {
40
- this.endpoint = options.endpoint
41
- this.headers = {
42
- 'Content-Type': 'application/json',
43
- ...options.headers,
44
- }
45
- }
46
-
47
- /**
48
- * Execute a GraphQL query against the configured endpoint.
49
- *
50
- * @param query - GraphQL query string
51
- * @param variables - Query variables
52
- * @throws Error if the GraphQL response contains errors
53
- */
54
- async query<T = unknown>(query: string, variables?: Record<string, unknown>): Promise<T> {
55
- const response = await fetch(this.endpoint, {
56
- method: 'POST',
57
- headers: this.headers,
58
- body: JSON.stringify({ query, variables }),
59
- })
60
-
61
- const json: { data?: T; errors?: Array<{ message: string }> } = await response.json()
62
-
63
- if (json.errors?.length) {
64
- throw new Error(json.errors[0].message)
65
- }
66
-
67
- if (json.data === undefined) {
68
- throw new Error('GraphQL response missing data field')
69
- }
70
-
71
- return json.data
72
- }
73
-
74
- /**
75
- * Execute a GraphQL mutation. Delegates to query() since both use POST.
76
- *
77
- * @param mutation - GraphQL mutation string
78
- * @param variables - Mutation variables
79
- */
80
- async mutate<T = unknown>(mutation: string, variables?: Record<string, unknown>): Promise<T> {
81
- return this.query<T>(mutation, variables)
82
- }
83
-
84
- /**
85
- * Get doctype metadata
86
- * @param context - Doctype context containing doctype name
87
- */
88
- async getMeta(context: DoctypeContext): Promise<DoctypeMeta | null> {
89
- const cached = this.metaCache.get(context.doctype)
90
- if (cached) return cached
91
-
92
- const result = await this.query<{ stonecropMeta: DoctypeMeta | null }>(GET_META_QUERY, {
93
- doctype: context.doctype,
94
- })
95
-
96
- if (result.stonecropMeta) {
97
- this.metaCache.set(context.doctype, result.stonecropMeta)
98
- }
99
-
100
- return result.stonecropMeta
101
- }
102
-
103
- /**
104
- * Get all doctype metadata
105
- */
106
- async getAllMeta(): Promise<DoctypeMeta[]> {
107
- const result = await this.query<{ stonecropAllMeta: DoctypeMeta[] }>(GET_ALL_META_QUERY)
108
-
109
- for (const meta of result.stonecropAllMeta) {
110
- this.metaCache.set(meta.name, meta)
111
- }
112
-
113
- return result.stonecropAllMeta
114
- }
115
-
116
- /**
117
- * Get a single record by ID.
118
- *
119
- * Routes through the stonecropRecord resolver which handles nested data
120
- * fetching based on the includeNested option.
121
- *
122
- * @param doctype - Doctype reference (name and optional slug)
123
- * @param recordId - Record ID to fetch
124
- * @param options - Query options (includeNested, maxDepth)
125
- */
126
- async getRecord(doctype: DoctypeRef, recordId: string, options?: GetRecordOptions): Promise<GetRecordResult> {
127
- const result = await this.query<{
128
- stonecropRecord: { data: Record<string, unknown> | null; unknownLinks?: string[] }
129
- }>(
130
- `query GetRecord($doctype: String!, $id: String!, $options: JSON) {
131
- stonecropRecord(doctype: $doctype, id: $id, options: $options) {
132
- data
133
- unknownLinks
134
- }
135
- }`,
136
- {
137
- doctype: doctype.name,
138
- id: recordId,
139
- options: options?.includeNested
140
- ? {
141
- includeNested: options.includeNested,
142
- maxDepth: options.maxDepth,
143
- }
144
- : undefined,
145
- }
146
- )
147
-
148
- return {
149
- record: result.stonecropRecord?.data ?? null,
150
- unknownLinks: result.stonecropRecord?.unknownLinks,
151
- }
152
- }
153
-
154
- /**
155
- * Get multiple records with optional filtering and pagination.
156
- *
157
- * Returns flat arrays — the middleware merges connection format (\{ nodes: [...] \})
158
- * into plain arrays before returning.
159
- *
160
- * @param doctype - Doctype reference (name and optional slug)
161
- * @param options - Query options (filters, orderBy, limit, offset)
162
- */
163
- async getRecords(doctype: DoctypeRef, options?: GetRecordsOptions): Promise<GetRecordsResult> {
164
- const result = await this.query<{
165
- stonecropRecords: { data: Record<string, unknown>[]; hasMore: boolean; count: number | null }
166
- }>(
167
- `
168
- query GetRecords(
169
- $doctype: String!
170
- $filters: JSON
171
- $orderBy: String
172
- $limit: Int
173
- $offset: Int
174
- $includeTotal: Boolean
175
- ) {
176
- stonecropRecords(
177
- doctype: $doctype
178
- filters: $filters
179
- orderBy: $orderBy
180
- limit: $limit
181
- offset: $offset
182
- includeTotal: $includeTotal
183
- ) {
184
- data
185
- hasMore
186
- count
187
- }
188
- }
189
- `,
190
- {
191
- doctype: doctype.name,
192
- ...options,
193
- }
194
- )
195
-
196
- const { data, hasMore, count } = result.stonecropRecords
197
- // `count` is null unless includeTotal was set. Omitting the key rather than passing null
198
- // through keeps "not asked for" and "asked for, and it is zero" distinguishable.
199
- return count == null ? { data, hasMore } : { data, hasMore, count }
200
- }
201
-
202
- /**
203
- * Execute a doctype action
204
- * @param doctype - Doctype reference (name and optional slug)
205
- * @param action - Action name to execute
206
- * @param args - Action arguments
207
- */
208
- async runAction(
209
- doctype: DoctypeRef,
210
- action: string,
211
- args?: unknown[]
212
- ): Promise<{ success: boolean; data: unknown; error: string | null }> {
213
- const result = await this.query<{
214
- stonecropAction: { success: boolean; data: unknown; error: string | null }
215
- }>(RUN_ACTION_MUTATION, {
216
- doctype: doctype.name,
217
- action,
218
- args,
219
- })
220
-
221
- return result.stonecropAction
222
- }
223
-
224
- /**
225
- * Clear the cached doctype metadata.
226
- *
227
- * Call this if the server-side doctype schema has changed and you need
228
- * to fetch fresh metadata (e.g., after adding a new field).
229
- */
230
- clearMetaCache(): void {
231
- this.metaCache.clear()
232
- }
233
- }
package/src/index.ts DELETED
@@ -1,4 +0,0 @@
1
- export type { DoctypeMeta } from '@stonecrop/schema'
2
-
3
- export { StonecropClient, type StonecropClientOptions, type DoctypeContext } from './client'
4
- export type { GetRecordResult } from './types'