@unchainedshop/cockpit-api 1.0.34 → 2.1.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 +330 -65
- package/dist/client.d.ts +72 -0
- package/dist/client.js +101 -0
- package/dist/cockpit-logger.d.ts +8 -5
- package/dist/cockpit-logger.js +7 -20
- package/dist/core/cache.d.ts +19 -0
- package/dist/core/cache.js +32 -0
- package/dist/core/config.d.ts +45 -0
- package/dist/core/config.js +32 -0
- package/dist/core/http.d.ts +34 -0
- package/dist/core/http.js +98 -0
- package/dist/core/index.d.ts +14 -0
- package/dist/core/index.js +10 -0
- package/dist/core/locale.d.ts +11 -0
- package/dist/core/locale.js +17 -0
- package/dist/core/query-string.d.ts +18 -0
- package/dist/core/query-string.js +20 -0
- package/dist/core/url-builder.d.ts +22 -0
- package/dist/core/url-builder.js +35 -0
- package/dist/core/validation.d.ts +13 -0
- package/dist/core/validation.js +22 -0
- package/dist/fetch/client.d.ts +85 -0
- package/dist/fetch/client.js +143 -0
- package/dist/fetch/index.d.ts +19 -0
- package/dist/fetch/index.js +18 -0
- package/dist/index.d.ts +28 -2
- package/dist/index.js +13 -2
- package/dist/methods/assets.d.ts +65 -0
- package/dist/methods/assets.js +37 -0
- package/dist/methods/content.d.ts +77 -0
- package/dist/methods/content.js +79 -0
- package/dist/methods/graphql.d.ts +9 -0
- package/dist/methods/graphql.js +13 -0
- package/dist/methods/index.d.ts +22 -0
- package/dist/methods/index.js +12 -0
- package/dist/methods/localize.d.ts +12 -0
- package/dist/methods/localize.js +17 -0
- package/dist/methods/menus.d.ts +42 -0
- package/dist/methods/menus.js +25 -0
- package/dist/methods/pages.d.ts +49 -0
- package/dist/methods/pages.js +34 -0
- package/dist/methods/routes.d.ts +46 -0
- package/dist/methods/routes.js +24 -0
- package/dist/methods/search.d.ts +27 -0
- package/dist/methods/search.js +16 -0
- package/dist/methods/system.d.ts +15 -0
- package/dist/methods/system.js +14 -0
- package/dist/schema/executor.d.ts +67 -0
- package/dist/schema/executor.js +81 -0
- package/dist/schema/index.d.ts +26 -0
- package/dist/schema/index.js +26 -0
- package/dist/schema/schema-builder.d.ts +42 -0
- package/dist/schema/schema-builder.js +77 -0
- package/dist/transformers/asset-path.d.ts +20 -0
- package/dist/transformers/asset-path.js +40 -0
- package/dist/transformers/compose.d.ts +9 -0
- package/dist/transformers/compose.js +14 -0
- package/dist/transformers/image-path.d.ts +28 -0
- package/dist/transformers/image-path.js +60 -0
- package/dist/transformers/index.d.ts +8 -0
- package/dist/transformers/index.js +9 -0
- package/dist/transformers/page-link.d.ts +18 -0
- package/dist/transformers/page-link.js +45 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.js +5 -0
- package/dist/utils/route-map.d.ts +12 -0
- package/dist/utils/route-map.js +86 -0
- package/dist/utils/tenant.d.ts +71 -0
- package/dist/utils/tenant.js +88 -0
- package/package.json +49 -7
- package/dist/api.d.ts +0 -69
- package/dist/api.js +0 -303
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search API methods (Detektivo addon)
|
|
3
|
+
*/
|
|
4
|
+
import type { MethodContext } from "./content.ts";
|
|
5
|
+
export interface SearchQueryOptions {
|
|
6
|
+
index: string;
|
|
7
|
+
q?: string;
|
|
8
|
+
/** Comma-separated list of fields to retrieve */
|
|
9
|
+
fields?: string;
|
|
10
|
+
limit?: number;
|
|
11
|
+
offset?: number;
|
|
12
|
+
}
|
|
13
|
+
export interface CockpitSearchHit {
|
|
14
|
+
_id: string;
|
|
15
|
+
_score: number;
|
|
16
|
+
_source: Record<string, unknown>;
|
|
17
|
+
highlight?: Record<string, string[]>;
|
|
18
|
+
}
|
|
19
|
+
export interface CockpitSearchResult {
|
|
20
|
+
hits: CockpitSearchHit[];
|
|
21
|
+
total: number;
|
|
22
|
+
took: number;
|
|
23
|
+
}
|
|
24
|
+
export interface SearchMethods {
|
|
25
|
+
search<T = CockpitSearchResult>(options: SearchQueryOptions): Promise<T | null>;
|
|
26
|
+
}
|
|
27
|
+
export declare function createSearchMethods(ctx: MethodContext): SearchMethods;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search API methods (Detektivo addon)
|
|
3
|
+
*/
|
|
4
|
+
import { requireParam } from "../core/validation.js";
|
|
5
|
+
export function createSearchMethods(ctx) {
|
|
6
|
+
return {
|
|
7
|
+
async search(options) {
|
|
8
|
+
const { index, q, fields, limit, offset } = options;
|
|
9
|
+
requireParam(index, "a search index");
|
|
10
|
+
const url = ctx.url.build(`/detektivo/search/${index}`, {
|
|
11
|
+
queryParams: { q, fields, limit, offset },
|
|
12
|
+
});
|
|
13
|
+
return ctx.http.fetch(url);
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* System API methods
|
|
3
|
+
*/
|
|
4
|
+
import type { MethodContext } from "./content.ts";
|
|
5
|
+
export interface CockpitHealthCheck {
|
|
6
|
+
status: "ok" | "error";
|
|
7
|
+
message?: string;
|
|
8
|
+
version?: string;
|
|
9
|
+
timestamp?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface SystemMethods {
|
|
12
|
+
healthCheck<T = unknown>(): Promise<T | null>;
|
|
13
|
+
clearCache(pattern?: string): void;
|
|
14
|
+
}
|
|
15
|
+
export declare function createSystemMethods(ctx: MethodContext): SystemMethods;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* System API methods
|
|
3
|
+
*/
|
|
4
|
+
export function createSystemMethods(ctx) {
|
|
5
|
+
return {
|
|
6
|
+
async healthCheck() {
|
|
7
|
+
const url = ctx.url.build("/system/healthcheck");
|
|
8
|
+
return ctx.http.fetch(url);
|
|
9
|
+
},
|
|
10
|
+
clearCache(pattern) {
|
|
11
|
+
ctx.cache.clear(pattern);
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remote executor for Cockpit GraphQL schema stitching
|
|
3
|
+
*/
|
|
4
|
+
import type { DocumentNode } from "graphql";
|
|
5
|
+
import type { CockpitAPIOptions } from "../core/config.ts";
|
|
6
|
+
/**
|
|
7
|
+
* Context passed to the remote executor
|
|
8
|
+
* Typically includes the request object for extracting tenant info
|
|
9
|
+
*/
|
|
10
|
+
export interface CockpitExecutorContext {
|
|
11
|
+
req?: {
|
|
12
|
+
headers?: Record<string, string | string[] | undefined>;
|
|
13
|
+
};
|
|
14
|
+
[key: string]: unknown;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Options for creating the Cockpit GraphQL schema
|
|
18
|
+
*/
|
|
19
|
+
export interface MakeCockpitSchemaOptions {
|
|
20
|
+
/** Header name to extract tenant from (default: "x-cockpit-space") */
|
|
21
|
+
tenantHeader?: string;
|
|
22
|
+
/** Filter out mutations from the schema (default: true) */
|
|
23
|
+
filterMutations?: boolean;
|
|
24
|
+
/** Additional transforms to apply to the schema */
|
|
25
|
+
transforms?: unknown[];
|
|
26
|
+
/** Custom tenant extractor function */
|
|
27
|
+
extractTenant?: (context: CockpitExecutorContext | undefined) => string | undefined;
|
|
28
|
+
/** CockpitAPI options to pass through (endpoint, apiKey, useAdminAccess) */
|
|
29
|
+
cockpitOptions?: Pick<CockpitAPIOptions, "endpoint" | "apiKey" | "useAdminAccess">;
|
|
30
|
+
/** Maximum number of clients to keep in the pool (default: 100) */
|
|
31
|
+
maxClients?: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Executor request signature for GraphQL Tools
|
|
35
|
+
*/
|
|
36
|
+
export interface ExecutorRequest {
|
|
37
|
+
document: DocumentNode;
|
|
38
|
+
variables?: Record<string, unknown>;
|
|
39
|
+
context?: CockpitExecutorContext;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Remote executor function type
|
|
43
|
+
*/
|
|
44
|
+
export type RemoteExecutor = (request: ExecutorRequest) => Promise<unknown>;
|
|
45
|
+
/**
|
|
46
|
+
* Creates a remote executor for Cockpit GraphQL
|
|
47
|
+
*
|
|
48
|
+
* The executor resolves the tenant from context and reuses
|
|
49
|
+
* CockpitAPI clients per tenant for efficiency.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* import { createRemoteExecutor } from "@unchainedshop/cockpit-api/schema";
|
|
54
|
+
*
|
|
55
|
+
* const executor = createRemoteExecutor({
|
|
56
|
+
* tenantHeader: "x-cockpit-space",
|
|
57
|
+
* });
|
|
58
|
+
*
|
|
59
|
+
* // Use with custom schema tooling
|
|
60
|
+
* const result = await executor({
|
|
61
|
+
* document: someQuery,
|
|
62
|
+
* variables: { id: "123" },
|
|
63
|
+
* context: { req: request },
|
|
64
|
+
* });
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export declare function createRemoteExecutor(options?: MakeCockpitSchemaOptions): RemoteExecutor;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remote executor for Cockpit GraphQL schema stitching
|
|
3
|
+
*/
|
|
4
|
+
import { LRUCache } from "lru-cache";
|
|
5
|
+
import { CockpitAPI } from "../client.js";
|
|
6
|
+
/** Default max clients to keep in pool */
|
|
7
|
+
const DEFAULT_MAX_CLIENTS = 100;
|
|
8
|
+
/**
|
|
9
|
+
* Default tenant extractor - reads from request header
|
|
10
|
+
*/
|
|
11
|
+
function defaultTenantExtractor(context, headerName) {
|
|
12
|
+
if (!context?.req?.headers)
|
|
13
|
+
return undefined;
|
|
14
|
+
const value = context.req.headers[headerName];
|
|
15
|
+
return Array.isArray(value) ? value[0] : value;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Creates a remote executor for Cockpit GraphQL
|
|
19
|
+
*
|
|
20
|
+
* The executor resolves the tenant from context and reuses
|
|
21
|
+
* CockpitAPI clients per tenant for efficiency.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* import { createRemoteExecutor } from "@unchainedshop/cockpit-api/schema";
|
|
26
|
+
*
|
|
27
|
+
* const executor = createRemoteExecutor({
|
|
28
|
+
* tenantHeader: "x-cockpit-space",
|
|
29
|
+
* });
|
|
30
|
+
*
|
|
31
|
+
* // Use with custom schema tooling
|
|
32
|
+
* const result = await executor({
|
|
33
|
+
* document: someQuery,
|
|
34
|
+
* variables: { id: "123" },
|
|
35
|
+
* context: { req: request },
|
|
36
|
+
* });
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export function createRemoteExecutor(options = {}) {
|
|
40
|
+
const { tenantHeader = "x-cockpit-space", extractTenant, cockpitOptions = {}, maxClients = DEFAULT_MAX_CLIENTS, } = options;
|
|
41
|
+
// Client pool with LRU eviction to prevent unbounded memory growth
|
|
42
|
+
const clientPool = new LRUCache({
|
|
43
|
+
max: maxClients,
|
|
44
|
+
});
|
|
45
|
+
const pendingClients = new Map();
|
|
46
|
+
async function getOrCreateClient(tenant) {
|
|
47
|
+
const key = tenant ?? "__default__";
|
|
48
|
+
// Return cached client if available
|
|
49
|
+
const cached = clientPool.get(key);
|
|
50
|
+
if (cached)
|
|
51
|
+
return cached;
|
|
52
|
+
// If client is being created, wait for it
|
|
53
|
+
const pending = pendingClients.get(key);
|
|
54
|
+
if (pending)
|
|
55
|
+
return pending;
|
|
56
|
+
// Create new client and cache it
|
|
57
|
+
const clientOpts = { ...cockpitOptions };
|
|
58
|
+
if (tenant !== undefined)
|
|
59
|
+
clientOpts.tenant = tenant;
|
|
60
|
+
const clientPromise = CockpitAPI(clientOpts);
|
|
61
|
+
pendingClients.set(key, clientPromise);
|
|
62
|
+
try {
|
|
63
|
+
const client = await clientPromise;
|
|
64
|
+
clientPool.set(key, client);
|
|
65
|
+
return client;
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
pendingClients.delete(key);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return async ({ document, variables, context }) => {
|
|
72
|
+
// Extract tenant from context
|
|
73
|
+
const tenant = extractTenant
|
|
74
|
+
? extractTenant(context)
|
|
75
|
+
: defaultTenantExtractor(context, tenantHeader);
|
|
76
|
+
// Get or create pooled client
|
|
77
|
+
const cockpit = await getOrCreateClient(tenant);
|
|
78
|
+
// Execute GraphQL query
|
|
79
|
+
return cockpit.graphQL(document, variables);
|
|
80
|
+
};
|
|
81
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GraphQL Schema Stitching utilities for Cockpit CMS
|
|
3
|
+
*
|
|
4
|
+
* This module requires `@graphql-tools/wrap` as a peer dependency.
|
|
5
|
+
* Install it with: npm install @graphql-tools/wrap
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { makeCockpitGraphQLSchema } from "@unchainedshop/cockpit-api/schema";
|
|
10
|
+
*
|
|
11
|
+
* // Create a stitchable schema from Cockpit
|
|
12
|
+
* const cockpitSchema = await makeCockpitGraphQLSchema({
|
|
13
|
+
* tenantHeader: "x-cockpit-space", // Header for multi-tenant support
|
|
14
|
+
* filterMutations: true, // Read-only by default
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* // Use with @graphql-tools/stitch
|
|
18
|
+
* import { stitchSchemas } from "@graphql-tools/stitch";
|
|
19
|
+
*
|
|
20
|
+
* const gatewaySchema = stitchSchemas({
|
|
21
|
+
* subschemas: [{ schema: cockpitSchema }],
|
|
22
|
+
* });
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export { makeCockpitGraphQLSchema } from "./schema-builder.ts";
|
|
26
|
+
export { createRemoteExecutor, type MakeCockpitSchemaOptions, type CockpitExecutorContext, type ExecutorRequest, type RemoteExecutor, } from "./executor.ts";
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GraphQL Schema Stitching utilities for Cockpit CMS
|
|
3
|
+
*
|
|
4
|
+
* This module requires `@graphql-tools/wrap` as a peer dependency.
|
|
5
|
+
* Install it with: npm install @graphql-tools/wrap
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { makeCockpitGraphQLSchema } from "@unchainedshop/cockpit-api/schema";
|
|
10
|
+
*
|
|
11
|
+
* // Create a stitchable schema from Cockpit
|
|
12
|
+
* const cockpitSchema = await makeCockpitGraphQLSchema({
|
|
13
|
+
* tenantHeader: "x-cockpit-space", // Header for multi-tenant support
|
|
14
|
+
* filterMutations: true, // Read-only by default
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* // Use with @graphql-tools/stitch
|
|
18
|
+
* import { stitchSchemas } from "@graphql-tools/stitch";
|
|
19
|
+
*
|
|
20
|
+
* const gatewaySchema = stitchSchemas({
|
|
21
|
+
* subschemas: [{ schema: cockpitSchema }],
|
|
22
|
+
* });
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export { makeCockpitGraphQLSchema } from "./schema-builder.js";
|
|
26
|
+
export { createRemoteExecutor, } from "./executor.js";
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GraphQL schema builder for Cockpit CMS schema stitching
|
|
3
|
+
*/
|
|
4
|
+
import type { GraphQLSchema } from "graphql";
|
|
5
|
+
import { type MakeCockpitSchemaOptions } from "./executor.ts";
|
|
6
|
+
/**
|
|
7
|
+
* Creates a GraphQL schema from Cockpit's introspected schema for schema stitching
|
|
8
|
+
*
|
|
9
|
+
* Requires `@graphql-tools/wrap` as a peer dependency.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* import { makeCockpitGraphQLSchema } from "@unchainedshop/cockpit-api/schema";
|
|
14
|
+
*
|
|
15
|
+
* // Basic usage - creates read-only schema with tenant header support
|
|
16
|
+
* const schema = await makeCockpitGraphQLSchema();
|
|
17
|
+
*
|
|
18
|
+
* // Custom configuration
|
|
19
|
+
* const schema = await makeCockpitGraphQLSchema({
|
|
20
|
+
* tenantHeader: "x-cockpit-space",
|
|
21
|
+
* filterMutations: true,
|
|
22
|
+
* cockpitOptions: {
|
|
23
|
+
* endpoint: "https://cms.example.com/api/graphql",
|
|
24
|
+
* },
|
|
25
|
+
* });
|
|
26
|
+
*
|
|
27
|
+
* // Use with schema stitching
|
|
28
|
+
* import { stitchSchemas } from "@graphql-tools/stitch";
|
|
29
|
+
*
|
|
30
|
+
* const gatewaySchema = stitchSchemas({
|
|
31
|
+
* subschemas: [
|
|
32
|
+
* { schema: await makeCockpitGraphQLSchema() },
|
|
33
|
+
* // ... other subschemas
|
|
34
|
+
* ],
|
|
35
|
+
* });
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* @param options - Configuration options
|
|
39
|
+
* @returns Promise resolving to the GraphQL schema
|
|
40
|
+
* @throws Error if @graphql-tools/wrap is not installed
|
|
41
|
+
*/
|
|
42
|
+
export declare function makeCockpitGraphQLSchema(options?: MakeCockpitSchemaOptions): Promise<GraphQLSchema>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GraphQL schema builder for Cockpit CMS schema stitching
|
|
3
|
+
*/
|
|
4
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
5
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
6
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
7
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
return path;
|
|
11
|
+
};
|
|
12
|
+
import { createRemoteExecutor, } from "./executor.js";
|
|
13
|
+
/**
|
|
14
|
+
* Creates a GraphQL schema from Cockpit's introspected schema for schema stitching
|
|
15
|
+
*
|
|
16
|
+
* Requires `@graphql-tools/wrap` as a peer dependency.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* import { makeCockpitGraphQLSchema } from "@unchainedshop/cockpit-api/schema";
|
|
21
|
+
*
|
|
22
|
+
* // Basic usage - creates read-only schema with tenant header support
|
|
23
|
+
* const schema = await makeCockpitGraphQLSchema();
|
|
24
|
+
*
|
|
25
|
+
* // Custom configuration
|
|
26
|
+
* const schema = await makeCockpitGraphQLSchema({
|
|
27
|
+
* tenantHeader: "x-cockpit-space",
|
|
28
|
+
* filterMutations: true,
|
|
29
|
+
* cockpitOptions: {
|
|
30
|
+
* endpoint: "https://cms.example.com/api/graphql",
|
|
31
|
+
* },
|
|
32
|
+
* });
|
|
33
|
+
*
|
|
34
|
+
* // Use with schema stitching
|
|
35
|
+
* import { stitchSchemas } from "@graphql-tools/stitch";
|
|
36
|
+
*
|
|
37
|
+
* const gatewaySchema = stitchSchemas({
|
|
38
|
+
* subschemas: [
|
|
39
|
+
* { schema: await makeCockpitGraphQLSchema() },
|
|
40
|
+
* // ... other subschemas
|
|
41
|
+
* ],
|
|
42
|
+
* });
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* @param options - Configuration options
|
|
46
|
+
* @returns Promise resolving to the GraphQL schema
|
|
47
|
+
* @throws Error if @graphql-tools/wrap is not installed
|
|
48
|
+
*/
|
|
49
|
+
export async function makeCockpitGraphQLSchema(options = {}) {
|
|
50
|
+
// Dynamic import to handle optional peer dependency
|
|
51
|
+
let wrapModule;
|
|
52
|
+
try {
|
|
53
|
+
wrapModule = (await import(__rewriteRelativeImportExtension("@graphql-tools/wrap")));
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
throw new Error("Cockpit: @graphql-tools/wrap is required for schema stitching. " +
|
|
57
|
+
"Install it with: npm install @graphql-tools/wrap");
|
|
58
|
+
}
|
|
59
|
+
const { schemaFromExecutor, wrapSchema, FilterRootFields } = wrapModule;
|
|
60
|
+
const { filterMutations = true, transforms = [] } = options;
|
|
61
|
+
const executor = createRemoteExecutor(options);
|
|
62
|
+
// Build the list of transforms
|
|
63
|
+
const allTransforms = [];
|
|
64
|
+
// Add mutation filter if enabled (default)
|
|
65
|
+
if (filterMutations) {
|
|
66
|
+
allTransforms.push(new FilterRootFields((operationName) => operationName !== "Mutation"));
|
|
67
|
+
}
|
|
68
|
+
// Add user-provided transforms
|
|
69
|
+
allTransforms.push(...transforms);
|
|
70
|
+
// Introspect and wrap the schema
|
|
71
|
+
const introspectedSchema = await schemaFromExecutor(executor);
|
|
72
|
+
return wrapSchema({
|
|
73
|
+
schema: introspectedSchema,
|
|
74
|
+
executor,
|
|
75
|
+
transforms: allTransforms,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asset path transformer for fixing Cockpit CMS storage paths
|
|
3
|
+
*/
|
|
4
|
+
import type { ResponseTransformer } from "./image-path.ts";
|
|
5
|
+
export interface AssetPathConfig {
|
|
6
|
+
/** Base URL of the Cockpit CMS (origin only) */
|
|
7
|
+
baseUrl: string;
|
|
8
|
+
/** Tenant name for multi-tenant setups */
|
|
9
|
+
tenant?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Transforms asset paths in a JSON string.
|
|
13
|
+
* This is the core transformation logic used by the transformer.
|
|
14
|
+
*/
|
|
15
|
+
export declare function transformAssetPaths(jsonString: string, config: AssetPathConfig): string;
|
|
16
|
+
/**
|
|
17
|
+
* Creates a transformer that fixes asset paths in responses.
|
|
18
|
+
* Use this when you only need asset path fixing without page link resolution.
|
|
19
|
+
*/
|
|
20
|
+
export declare function createAssetPathTransformer(config: AssetPathConfig): ResponseTransformer;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asset path transformer for fixing Cockpit CMS storage paths
|
|
3
|
+
*/
|
|
4
|
+
import { logger } from "../cockpit-logger.js";
|
|
5
|
+
/**
|
|
6
|
+
* Transforms asset paths in a JSON string.
|
|
7
|
+
* This is the core transformation logic used by the transformer.
|
|
8
|
+
*/
|
|
9
|
+
export function transformAssetPaths(jsonString, config) {
|
|
10
|
+
const { baseUrl, tenant } = config;
|
|
11
|
+
const tenantUrl = `${baseUrl}${tenant !== undefined ? `/:${tenant}` : ""}`;
|
|
12
|
+
return (jsonString
|
|
13
|
+
// Fix asset paths in "path" fields
|
|
14
|
+
.replace(/"path":"\//g, `"path":"${tenantUrl}/storage/uploads/`)
|
|
15
|
+
// Fix storage paths in src attributes
|
|
16
|
+
.replace(/src=\\"(\/[^"]*?)storage/gi, `src=\\"${baseUrl}$1storage`)
|
|
17
|
+
// Fix storage paths in href attributes
|
|
18
|
+
.replace(/href=\\"(\/[^"]*?)storage/gi, `href=\\"${baseUrl}$1storage`)
|
|
19
|
+
// Fix duplicate storage/uploads paths (cleanup)
|
|
20
|
+
.replace(/\/storage\/uploads\/storage\/uploads\//g, `/storage/uploads/`));
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Creates a transformer that fixes asset paths in responses.
|
|
24
|
+
* Use this when you only need asset path fixing without page link resolution.
|
|
25
|
+
*/
|
|
26
|
+
export function createAssetPathTransformer(config) {
|
|
27
|
+
return {
|
|
28
|
+
transform(originalResponse) {
|
|
29
|
+
try {
|
|
30
|
+
const jsonString = JSON.stringify(originalResponse);
|
|
31
|
+
const fixedString = transformAssetPaths(jsonString, config);
|
|
32
|
+
return JSON.parse(fixedString);
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
logger.warn("Cockpit: Failed to transform asset paths", error);
|
|
36
|
+
return originalResponse;
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transformer composition utilities
|
|
3
|
+
*/
|
|
4
|
+
import type { ResponseTransformer } from "./image-path.ts";
|
|
5
|
+
/**
|
|
6
|
+
* Composes multiple transformers into a single transformer
|
|
7
|
+
* Transforms are applied left to right
|
|
8
|
+
*/
|
|
9
|
+
export declare function composeTransformers(...transformers: ResponseTransformer[]): ResponseTransformer;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transformer composition utilities
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Composes multiple transformers into a single transformer
|
|
6
|
+
* Transforms are applied left to right
|
|
7
|
+
*/
|
|
8
|
+
export function composeTransformers(...transformers) {
|
|
9
|
+
return {
|
|
10
|
+
transform(response) {
|
|
11
|
+
return transformers.reduce((result, transformer) => transformer.transform(result), response);
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image path transformer for fixing asset paths and page links
|
|
3
|
+
*
|
|
4
|
+
* This module provides the main transformer that combines asset path fixing
|
|
5
|
+
* and page link resolution in a single efficient pass. For individual
|
|
6
|
+
* transformers, see asset-path.ts and page-link.ts.
|
|
7
|
+
*/
|
|
8
|
+
import { type AssetPathConfig } from "./asset-path.ts";
|
|
9
|
+
export interface ResponseTransformer {
|
|
10
|
+
transform<T>(response: T): T;
|
|
11
|
+
}
|
|
12
|
+
export declare const identityTransformer: ResponseTransformer;
|
|
13
|
+
export interface ImagePathTransformerConfig extends AssetPathConfig {
|
|
14
|
+
/** Route replacements map (pages://id -> route) */
|
|
15
|
+
replacements: Record<string, string>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Creates a transformer that fixes image paths and resolves page links.
|
|
19
|
+
* Uses a single JSON stringify/parse cycle for efficiency.
|
|
20
|
+
*/
|
|
21
|
+
export declare function createImagePathTransformer(config: ImagePathTransformerConfig): ResponseTransformer;
|
|
22
|
+
/**
|
|
23
|
+
* Legacy export for backwards compatibility
|
|
24
|
+
* @deprecated Use createImagePathTransformer instead
|
|
25
|
+
*/
|
|
26
|
+
export declare function FixImagePaths(endpoint: string, replacements: Record<string, string>, tenant?: string): {
|
|
27
|
+
transformResult<T>(originalResponse: T): T;
|
|
28
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image path transformer for fixing asset paths and page links
|
|
3
|
+
*
|
|
4
|
+
* This module provides the main transformer that combines asset path fixing
|
|
5
|
+
* and page link resolution in a single efficient pass. For individual
|
|
6
|
+
* transformers, see asset-path.ts and page-link.ts.
|
|
7
|
+
*/
|
|
8
|
+
import { logger } from "../cockpit-logger.js";
|
|
9
|
+
import { transformAssetPaths } from "./asset-path.js";
|
|
10
|
+
import { transformPageLinks } from "./page-link.js";
|
|
11
|
+
export const identityTransformer = {
|
|
12
|
+
transform(response) {
|
|
13
|
+
return response;
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Creates a transformer that fixes image paths and resolves page links.
|
|
18
|
+
* Uses a single JSON stringify/parse cycle for efficiency.
|
|
19
|
+
*/
|
|
20
|
+
export function createImagePathTransformer(config) {
|
|
21
|
+
const { baseUrl, tenant, replacements } = config;
|
|
22
|
+
// Build asset config, only including tenant if defined
|
|
23
|
+
const assetConfig = { baseUrl };
|
|
24
|
+
if (tenant !== undefined)
|
|
25
|
+
assetConfig.tenant = tenant;
|
|
26
|
+
return {
|
|
27
|
+
transform(originalResponse) {
|
|
28
|
+
try {
|
|
29
|
+
// Single stringify for efficiency
|
|
30
|
+
let jsonString = JSON.stringify(originalResponse);
|
|
31
|
+
// Apply asset path transformations
|
|
32
|
+
jsonString = transformAssetPaths(jsonString, assetConfig);
|
|
33
|
+
// Apply page link transformations
|
|
34
|
+
jsonString = transformPageLinks(jsonString, replacements);
|
|
35
|
+
return JSON.parse(jsonString);
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
logger.warn("Cockpit: Failed to transform response", error);
|
|
39
|
+
return originalResponse;
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Legacy export for backwards compatibility
|
|
46
|
+
* @deprecated Use createImagePathTransformer instead
|
|
47
|
+
*/
|
|
48
|
+
export function FixImagePaths(endpoint, replacements, tenant) {
|
|
49
|
+
const baseUrl = new URL(endpoint).origin;
|
|
50
|
+
const config = {
|
|
51
|
+
baseUrl,
|
|
52
|
+
replacements,
|
|
53
|
+
};
|
|
54
|
+
if (tenant !== undefined)
|
|
55
|
+
config.tenant = tenant;
|
|
56
|
+
const transformer = createImagePathTransformer(config);
|
|
57
|
+
return {
|
|
58
|
+
transformResult: transformer.transform.bind(transformer),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transformer exports
|
|
3
|
+
*/
|
|
4
|
+
export type { ResponseTransformer } from "./image-path.ts";
|
|
5
|
+
export { identityTransformer, createImagePathTransformer, FixImagePaths, } from "./image-path.ts";
|
|
6
|
+
export { composeTransformers } from "./compose.ts";
|
|
7
|
+
export { createAssetPathTransformer, transformAssetPaths, type AssetPathConfig, } from "./asset-path.ts";
|
|
8
|
+
export { createPageLinkTransformer, transformPageLinks } from "./page-link.ts";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transformer exports
|
|
3
|
+
*/
|
|
4
|
+
export { identityTransformer, createImagePathTransformer,
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
6
|
+
FixImagePaths, } from "./image-path.js";
|
|
7
|
+
export { composeTransformers } from "./compose.js";
|
|
8
|
+
export { createAssetPathTransformer, transformAssetPaths, } from "./asset-path.js";
|
|
9
|
+
export { createPageLinkTransformer, transformPageLinks } from "./page-link.js";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page link transformer for resolving pages://id links
|
|
3
|
+
*/
|
|
4
|
+
import type { ResponseTransformer } from "./image-path.ts";
|
|
5
|
+
/**
|
|
6
|
+
* Transforms page links in a JSON string.
|
|
7
|
+
* This is the core transformation logic used by the transformer.
|
|
8
|
+
*
|
|
9
|
+
* @param jsonString - The JSON string to transform
|
|
10
|
+
* @param replacements - Map of pages://id -> actual route
|
|
11
|
+
* @returns Transformed JSON string with page links resolved
|
|
12
|
+
*/
|
|
13
|
+
export declare function transformPageLinks(jsonString: string, replacements: Record<string, string>): string;
|
|
14
|
+
/**
|
|
15
|
+
* Creates a transformer that resolves page links (pages://id -> route).
|
|
16
|
+
* Use this when you only need page link resolution without asset path fixing.
|
|
17
|
+
*/
|
|
18
|
+
export declare function createPageLinkTransformer(replacements: Record<string, string>): ResponseTransformer;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page link transformer for resolving pages://id links
|
|
3
|
+
*/
|
|
4
|
+
import { logger } from "../cockpit-logger.js";
|
|
5
|
+
/**
|
|
6
|
+
* Escapes regex special characters in a string
|
|
7
|
+
*/
|
|
8
|
+
const escapeRegex = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
9
|
+
/**
|
|
10
|
+
* Transforms page links in a JSON string.
|
|
11
|
+
* This is the core transformation logic used by the transformer.
|
|
12
|
+
*
|
|
13
|
+
* @param jsonString - The JSON string to transform
|
|
14
|
+
* @param replacements - Map of pages://id -> actual route
|
|
15
|
+
* @returns Transformed JSON string with page links resolved
|
|
16
|
+
*/
|
|
17
|
+
export function transformPageLinks(jsonString, replacements) {
|
|
18
|
+
const keys = Object.keys(replacements);
|
|
19
|
+
if (keys.length === 0)
|
|
20
|
+
return jsonString;
|
|
21
|
+
const pattern = new RegExp(keys.map(escapeRegex).join("|"), "g");
|
|
22
|
+
return jsonString.replace(pattern, (match) => replacements[match] ?? match);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Creates a transformer that resolves page links (pages://id -> route).
|
|
26
|
+
* Use this when you only need page link resolution without asset path fixing.
|
|
27
|
+
*/
|
|
28
|
+
export function createPageLinkTransformer(replacements) {
|
|
29
|
+
return {
|
|
30
|
+
transform(originalResponse) {
|
|
31
|
+
const keys = Object.keys(replacements);
|
|
32
|
+
if (keys.length === 0)
|
|
33
|
+
return originalResponse;
|
|
34
|
+
try {
|
|
35
|
+
const jsonString = JSON.stringify(originalResponse);
|
|
36
|
+
const fixedString = transformPageLinks(jsonString, replacements);
|
|
37
|
+
return JSON.parse(fixedString);
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
logger.warn("Cockpit: Failed to transform page links", error);
|
|
41
|
+
return originalResponse;
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|