@unchainedshop/cockpit-api 2.3.2 → 2.4.1

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/client.d.ts CHANGED
@@ -3,20 +3,26 @@
3
3
  */
4
4
  import type { DocumentNode } from "graphql";
5
5
  import { type CockpitAPIOptions } from "./core/config.ts";
6
- import { type ContentItemQueryOptions, type ContentListQueryOptions, type TreeQueryOptions, type AggregateQueryOptions, type CockpitContentItem, type CockpitTreeNode, type CockpitListResponse } from "./methods/content.ts";
6
+ import { type ContentItemQueryOptions, type ContentListQueryOptions, type UnchainedContentListQueryOptions, type TreeQueryOptions, type AggregateQueryOptions, type CockpitContentItem, type CockpitTreeNode, type CockpitListResponse } from "./methods/content.ts";
7
7
  import { type PageByIdOptions, type CockpitPage } from "./methods/pages.ts";
8
8
  import { type MenuQueryOptions, type CockpitMenu } from "./methods/menus.ts";
9
9
  import { type CockpitRoutesResponse, type CockpitSitemapEntry, type CockpitSettings } from "./methods/routes.ts";
10
- import { type ImageAssetQueryParams, type CockpitAsset } from "./methods/assets.ts";
10
+ import { type ImageAssetQueryParams, type CockpitAsset, type UploadAssetsOptions, type UploadAssetsResponse } from "./methods/assets.ts";
11
11
  import { type SearchQueryOptions, type CockpitSearchResult } from "./methods/search.ts";
12
12
  import { type LocalizeOptions } from "./methods/localize.ts";
13
13
  /**
14
14
  * Cockpit API Client interface
15
15
  */
16
16
  export interface CockpitAPIClient {
17
- graphQL<T = unknown>(document: DocumentNode, variables?: Record<string, unknown>): Promise<T | null>;
17
+ graphQL<T = unknown>(document: DocumentNode, variables?: Record<string, unknown>, operationName?: string): Promise<T | null>;
18
18
  getContentItem<T = unknown>(options: ContentItemQueryOptions): Promise<T | null>;
19
19
  getContentItems<T = CockpitContentItem>(model: string, options?: ContentListQueryOptions): Promise<CockpitListResponse<T> | null>;
20
+ /**
21
+ * Get content items including unpublished via Unchained module.
22
+ *
23
+ * Requires admin access with content/{model}/read permission.
24
+ */
25
+ getUnchainedContentItems<T = CockpitContentItem>(model: string, options?: UnchainedContentListQueryOptions): Promise<CockpitListResponse<T> | null>;
20
26
  getContentTree<T = CockpitContentItem>(model: string, options?: TreeQueryOptions): Promise<CockpitTreeNode<T>[] | null>;
21
27
  getAggregateModel<T = unknown>(options: AggregateQueryOptions): Promise<T[] | null>;
22
28
  postContentItem<T = unknown>(model: string, item: Record<string, unknown>): Promise<T | null>;
@@ -47,6 +53,12 @@ export interface CockpitAPIClient {
47
53
  * @returns URL string to the generated image, or null if not found
48
54
  */
49
55
  imageAssetById(assetId: string, queryParams: ImageAssetQueryParams): Promise<string | null>;
56
+ /**
57
+ * Upload assets to Cockpit CMS (Unchained module).
58
+ *
59
+ * Requires admin access (API key with assets/upload permission).
60
+ */
61
+ uploadAssets(files: File[], options?: UploadAssetsOptions): Promise<UploadAssetsResponse | null>;
50
62
  getFullRouteForSlug(slug: string): Promise<string | undefined>;
51
63
  /**
52
64
  * Clear cache entries matching pattern
@@ -23,6 +23,10 @@ export interface HttpClient {
23
23
  * Make a POST request with JSON body
24
24
  */
25
25
  post<T>(url: URL | string, body: unknown, options?: HttpFetchOptions): Promise<T | null>;
26
+ /**
27
+ * Make a POST request with FormData body (multipart/form-data)
28
+ */
29
+ postFormData<T>(url: URL | string, formData: FormData, options?: HttpFetchOptions): Promise<T | null>;
26
30
  /**
27
31
  * Make a DELETE request
28
32
  */
package/dist/core/http.js CHANGED
@@ -91,6 +91,18 @@ export function createHttpClient(config, transformer) {
91
91
  async post(url, body, options = {}) {
92
92
  return fetchData(url, prepareJsonRequestOptions(options, "POST", body));
93
93
  },
94
+ async postFormData(url, formData, options = {}) {
95
+ const { useAdminAccess, headers, ...restOptions } = options;
96
+ const customHeaders = normalizeHeaders(headers);
97
+ // Do NOT set Content-Type - let browser set it with correct boundary
98
+ return fetchData(url, {
99
+ ...restOptions,
100
+ method: "POST",
101
+ body: formData,
102
+ headers: customHeaders,
103
+ ...(useAdminAccess !== undefined && { useAdminAccess }),
104
+ });
105
+ },
94
106
  async delete(url, options = {}) {
95
107
  return fetchData(url, prepareJsonRequestOptions(options, "DELETE"));
96
108
  },
package/dist/index.d.ts CHANGED
@@ -13,14 +13,14 @@ export { generateCmsRouteReplacements, generateCollectionAndSingletonSlugRouteMa
13
13
  export type { ResponseTransformer } from "./transformers/image-path.ts";
14
14
  export { createImagePathTransformer, identityTransformer, } from "./transformers/image-path.ts";
15
15
  export { createAssetPathTransformer, createPageLinkTransformer, composeTransformers, } from "./transformers/index.ts";
16
- export type { MethodContext, ListQueryOptions, ContentItemQueryOptions, ContentListQueryOptions, TreeQueryOptions, AggregateQueryOptions, } from "./methods/content.ts";
16
+ export type { MethodContext, ListQueryOptions, ContentItemQueryOptions, ContentListQueryOptions, UnchainedContentListQueryOptions, TreeQueryOptions, AggregateQueryOptions, } from "./methods/content.ts";
17
17
  export type { PageByIdOptions, PageByRouteOptions } from "./methods/pages.ts";
18
18
  export type { MenuQueryOptions } from "./methods/menus.ts";
19
19
  export type { SearchQueryOptions } from "./methods/search.ts";
20
20
  export type { LocalizeOptions } from "./methods/localize.ts";
21
21
  export type { CockpitContentItem, CockpitNewsItem, CockpitTreeNode, CockpitListResponse, CockpitListMeta, } from "./methods/content.ts";
22
22
  export { ImageSizeMode, MimeType } from "./methods/assets.ts";
23
- export type { CockpitAsset, ImageAssetQueryParams } from "./methods/assets.ts";
23
+ export type { CockpitAsset, ImageAssetQueryParams, UploadAssetsOptions, UploadAssetsResponse, } from "./methods/assets.ts";
24
24
  export type { CockpitPageType, CockpitPageMeta, CockpitPageSeo, CockpitLayoutBlock, CockpitPage, } from "./methods/pages.ts";
25
25
  export type { CockpitMenuUrl, CockpitMenuLink, CockpitMenu, } from "./methods/menus.ts";
26
26
  export type { CockpitRoute, CockpitRoutesResponse, CockpitSitemapEntry, CockpitPreviewConfig, CockpitSettings, } from "./methods/routes.ts";
@@ -58,6 +58,19 @@ export type ImageAssetQueryParams = {
58
58
  w?: number;
59
59
  h: number;
60
60
  });
61
+ /**
62
+ * Options for uploading assets
63
+ */
64
+ export interface UploadAssetsOptions {
65
+ /** Target folder name for upload */
66
+ folder?: string;
67
+ }
68
+ /**
69
+ * Response from asset upload
70
+ */
71
+ export interface UploadAssetsResponse {
72
+ assets: CockpitAsset[];
73
+ }
61
74
  export interface AssetMethods {
62
75
  assetById<T = CockpitAsset>(assetId: string): Promise<T | null>;
63
76
  /**
@@ -71,5 +84,22 @@ export interface AssetMethods {
71
84
  * @returns URL string to the generated image, or null if not found
72
85
  */
73
86
  imageAssetById(assetId: string, queryParams: ImageAssetQueryParams): Promise<string | null>;
87
+ /**
88
+ * Upload assets to Cockpit CMS (Unchained module).
89
+ *
90
+ * Requires admin access (API key with assets/upload permission).
91
+ *
92
+ * @param files - Files to upload (File objects or Blob with name)
93
+ * @param options - Upload options (optional folder name)
94
+ * @returns Uploaded assets metadata
95
+ *
96
+ * @example
97
+ * ```typescript
98
+ * const file = new File(['content'], 'test.txt', { type: 'text/plain' });
99
+ * const result = await client.uploadAssets([file], { folder: 'documents' });
100
+ * console.log(result?.assets);
101
+ * ```
102
+ */
103
+ uploadAssets(files: File[], options?: UploadAssetsOptions): Promise<UploadAssetsResponse | null>;
74
104
  }
75
105
  export declare function createAssetMethods(ctx: MethodContext): AssetMethods;
@@ -33,5 +33,23 @@ export function createAssetMethods(ctx) {
33
33
  });
34
34
  return ctx.http.fetchText(url);
35
35
  },
36
+ async uploadAssets(files, options = {}) {
37
+ requireParam(files, "files");
38
+ if (files.length === 0) {
39
+ return { assets: [] };
40
+ }
41
+ const formData = new FormData();
42
+ for (const file of files) {
43
+ formData.append("files[]", file);
44
+ }
45
+ const url = ctx.url.build("/unchained/assets/upload", {
46
+ queryParams: {
47
+ ...(options.folder && { folder: options.folder }),
48
+ },
49
+ });
50
+ return ctx.http.postFormData(url, formData, {
51
+ useAdminAccess: true,
52
+ });
53
+ },
36
54
  };
37
55
  }
@@ -30,6 +30,16 @@ export interface ContentItemQueryOptions extends ListQueryOptions {
30
30
  export interface ContentListQueryOptions extends ListQueryOptions {
31
31
  queryParams?: Record<string, unknown>;
32
32
  }
33
+ /**
34
+ * Query options for Unchained content endpoint (supports unpublished items)
35
+ */
36
+ export interface UnchainedContentListQueryOptions extends ContentListQueryOptions {
37
+ /**
38
+ * Include unpublished items in results.
39
+ * Requires admin access with content/{model}/read permission.
40
+ */
41
+ includeUnpublished?: boolean;
42
+ }
33
43
  export interface TreeQueryOptions {
34
44
  parent?: string;
35
45
  filter?: Record<string, unknown>;
@@ -101,6 +111,24 @@ export interface ContentMethods {
101
111
  * const total = response?.meta?.total;
102
112
  */
103
113
  getContentItems<T = CockpitContentItem>(model: string, options?: ContentListQueryOptions): Promise<CockpitListResponse<T> | null>;
114
+ /**
115
+ * Get content items including unpublished via Unchained module.
116
+ *
117
+ * Requires admin access with content/{model}/read permission.
118
+ *
119
+ * @returns Always returns `CockpitListResponse<T>` with data array.
120
+ * Returns `null` if model doesn't exist.
121
+ *
122
+ * @example
123
+ * ```typescript
124
+ * const response = await cockpit.getUnchainedContentItems('posts', {
125
+ * limit: 10,
126
+ * includeUnpublished: true,
127
+ * });
128
+ * const items = response?.data || [];
129
+ * ```
130
+ */
131
+ getUnchainedContentItems<T = CockpitContentItem>(model: string, options?: UnchainedContentListQueryOptions): Promise<CockpitListResponse<T> | null>;
104
132
  getContentTree<T = CockpitContentItem>(model: string, options?: TreeQueryOptions): Promise<CockpitTreeNode<T>[] | null>;
105
133
  getAggregateModel<T = unknown>(options: AggregateQueryOptions): Promise<T[] | null>;
106
134
  postContentItem<T = unknown>(model: string, item: Record<string, unknown>): Promise<T | null>;
@@ -43,6 +43,35 @@ export function createContentMethods(ctx) {
43
43
  }
44
44
  return result;
45
45
  },
46
+ async getUnchainedContentItems(model, options = {}) {
47
+ requireParam(model, "a model");
48
+ validatePathSegment(model, "model");
49
+ const { locale = "default", limit, skip, sort, filter, fields, populate, includeUnpublished, queryParams = {}, } = options;
50
+ const url = ctx.url.build(`/unchained/content/items/${model}`, {
51
+ locale,
52
+ queryParams: {
53
+ ...queryParams,
54
+ limit,
55
+ skip,
56
+ sort,
57
+ filter,
58
+ fields,
59
+ populate,
60
+ ...(includeUnpublished && { includeUnpublished: 1 }),
61
+ },
62
+ });
63
+ const result = await ctx.http.fetch(url, {
64
+ useAdminAccess: true,
65
+ });
66
+ // Normalize response to always return { data, meta? }
67
+ if (result === null) {
68
+ return null;
69
+ }
70
+ if (Array.isArray(result)) {
71
+ return { data: result };
72
+ }
73
+ return result;
74
+ },
46
75
  async getContentTree(model, options = {}) {
47
76
  requireParam(model, "a model");
48
77
  validatePathSegment(model, "model");
@@ -4,6 +4,6 @@
4
4
  import { type DocumentNode } from "graphql";
5
5
  import type { MethodContext } from "./content.ts";
6
6
  export interface GraphQLMethods {
7
- graphQL<T = unknown>(document: DocumentNode, variables?: Record<string, unknown>): Promise<T | null>;
7
+ graphQL<T = unknown>(document: DocumentNode, variables?: Record<string, unknown>, operationName?: string): Promise<T | null>;
8
8
  }
9
9
  export declare function createGraphQLMethods(ctx: MethodContext): GraphQLMethods;
@@ -1,13 +1,18 @@
1
1
  /**
2
2
  * GraphQL API method
3
3
  */
4
- import { print } from "graphql";
4
+ import { print, getOperationAST } from "graphql";
5
5
  export function createGraphQLMethods(ctx) {
6
6
  return {
7
- async graphQL(document, variables) {
7
+ async graphQL(document, variables, operationName) {
8
8
  const query = print(document);
9
+ const resolvedOperationName = operationName ?? getOperationAST(document)?.name?.value;
9
10
  const endpoint = ctx.url.graphqlEndpoint();
10
- return ctx.http.post(endpoint, { query, variables });
11
+ return ctx.http.post(endpoint, {
12
+ query,
13
+ variables,
14
+ operationName: resolvedOperationName,
15
+ });
11
16
  },
12
17
  };
13
18
  }
@@ -36,6 +36,7 @@ export interface MakeCockpitSchemaOptions {
36
36
  export interface ExecutorRequest {
37
37
  document: DocumentNode;
38
38
  variables?: Record<string, unknown>;
39
+ operationName?: string;
39
40
  context?: CockpitExecutorContext;
40
41
  }
41
42
  /**
@@ -72,7 +72,7 @@ export function createRemoteExecutor(options = {}) {
72
72
  pendingClients.delete(key);
73
73
  }
74
74
  }
75
- return async ({ document, variables, context }) => {
75
+ return async ({ document, variables, operationName, context, }) => {
76
76
  // Extract tenant from context
77
77
  const tenant = extractTenant
78
78
  ? extractTenant(context)
@@ -80,6 +80,6 @@ export function createRemoteExecutor(options = {}) {
80
80
  // Get or create pooled client
81
81
  const cockpit = await getOrCreateClient(tenant);
82
82
  // Execute GraphQL query
83
- return cockpit.graphQL(document, variables);
83
+ return cockpit.graphQL(document, variables, operationName);
84
84
  };
85
85
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unchainedshop/cockpit-api",
3
- "version": "2.3.2",
3
+ "version": "2.4.1",
4
4
  "description": "A package to interact with the Cockpit CMS API, including functionalities to handle GraphQL requests and various CMS content manipulations.",
5
5
  "main": "dist/index.js",
6
6
  "homepage": "https://unchained.shop",
@@ -50,8 +50,8 @@
50
50
  "node": ">=20"
51
51
  },
52
52
  "dependencies": {
53
- "@unchainedshop/logger": "^4.6.0",
54
- "lru-cache": "^11.2.4"
53
+ "@unchainedshop/logger": "^4.6.2",
54
+ "lru-cache": "^11.2.6"
55
55
  },
56
56
  "types": "dist/index.d.ts",
57
57
  "peerDependencies": {
@@ -68,13 +68,13 @@
68
68
  },
69
69
  "devDependencies": {
70
70
  "@eslint/js": "^9.39.2",
71
- "@types/node": "^24.10.7",
71
+ "@types/node": "^25.3.1",
72
72
  "eslint": "^9.39.2",
73
73
  "eslint-config-prettier": "^10.1.8",
74
- "eslint-plugin-prettier": "^5.5.4",
75
- "graphql": "^16.12.0",
76
- "prettier": "^3.7.4",
74
+ "eslint-plugin-prettier": "^5.5.5",
75
+ "graphql": "^16.13.0",
76
+ "prettier": "^3.8.1",
77
77
  "typescript": "^5.9.3",
78
- "typescript-eslint": "^8.52.0"
78
+ "typescript-eslint": "^8.56.1"
79
79
  }
80
80
  }