@unchainedshop/cockpit-api 1.0.33 → 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.
Files changed (81) hide show
  1. package/README.md +330 -65
  2. package/dist/client.d.ts +72 -0
  3. package/dist/client.js +101 -0
  4. package/dist/cockpit-logger.d.ts +8 -10
  5. package/dist/cockpit-logger.js +7 -21
  6. package/dist/core/cache.d.ts +19 -0
  7. package/dist/core/cache.js +32 -0
  8. package/dist/core/config.d.ts +45 -0
  9. package/dist/core/config.js +32 -0
  10. package/dist/core/http.d.ts +34 -0
  11. package/dist/core/http.js +98 -0
  12. package/dist/core/index.d.ts +14 -0
  13. package/dist/core/index.js +10 -0
  14. package/dist/core/locale.d.ts +11 -0
  15. package/dist/core/locale.js +17 -0
  16. package/dist/core/query-string.d.ts +18 -0
  17. package/dist/core/query-string.js +20 -0
  18. package/dist/core/url-builder.d.ts +22 -0
  19. package/dist/core/url-builder.js +35 -0
  20. package/dist/core/validation.d.ts +13 -0
  21. package/dist/core/validation.js +22 -0
  22. package/dist/fetch/client.d.ts +85 -0
  23. package/dist/fetch/client.js +143 -0
  24. package/dist/fetch/index.d.ts +19 -0
  25. package/dist/fetch/index.js +18 -0
  26. package/dist/index.d.ts +28 -2
  27. package/dist/index.js +13 -3
  28. package/dist/methods/assets.d.ts +65 -0
  29. package/dist/methods/assets.js +37 -0
  30. package/dist/methods/content.d.ts +77 -0
  31. package/dist/methods/content.js +79 -0
  32. package/dist/methods/graphql.d.ts +9 -0
  33. package/dist/methods/graphql.js +13 -0
  34. package/dist/methods/index.d.ts +22 -0
  35. package/dist/methods/index.js +12 -0
  36. package/dist/methods/localize.d.ts +12 -0
  37. package/dist/methods/localize.js +17 -0
  38. package/dist/methods/menus.d.ts +42 -0
  39. package/dist/methods/menus.js +25 -0
  40. package/dist/methods/pages.d.ts +49 -0
  41. package/dist/methods/pages.js +34 -0
  42. package/dist/methods/routes.d.ts +46 -0
  43. package/dist/methods/routes.js +24 -0
  44. package/dist/methods/search.d.ts +27 -0
  45. package/dist/methods/search.js +16 -0
  46. package/dist/methods/system.d.ts +15 -0
  47. package/dist/methods/system.js +14 -0
  48. package/dist/schema/executor.d.ts +67 -0
  49. package/dist/schema/executor.js +81 -0
  50. package/dist/schema/index.d.ts +26 -0
  51. package/dist/schema/index.js +26 -0
  52. package/dist/schema/schema-builder.d.ts +42 -0
  53. package/dist/schema/schema-builder.js +77 -0
  54. package/dist/transformers/asset-path.d.ts +20 -0
  55. package/dist/transformers/asset-path.js +40 -0
  56. package/dist/transformers/compose.d.ts +9 -0
  57. package/dist/transformers/compose.js +14 -0
  58. package/dist/transformers/image-path.d.ts +28 -0
  59. package/dist/transformers/image-path.js +60 -0
  60. package/dist/transformers/index.d.ts +8 -0
  61. package/dist/transformers/index.js +9 -0
  62. package/dist/transformers/page-link.d.ts +18 -0
  63. package/dist/transformers/page-link.js +45 -0
  64. package/dist/utils/index.d.ts +5 -0
  65. package/dist/utils/index.js +5 -0
  66. package/dist/utils/route-map.d.ts +12 -0
  67. package/dist/utils/route-map.js +86 -0
  68. package/dist/utils/tenant.d.ts +71 -0
  69. package/dist/utils/tenant.js +88 -0
  70. package/package.json +50 -8
  71. package/.github/workflows/npm-publish.yml +0 -33
  72. package/.nvmrc +0 -1
  73. package/dist/api.d.ts +0 -70
  74. package/dist/api.js +0 -303
  75. package/dist/api.js.map +0 -1
  76. package/dist/cockpit-logger.js.map +0 -1
  77. package/dist/index.js.map +0 -1
  78. package/src/api.ts +0 -350
  79. package/src/cockpit-logger.ts +0 -24
  80. package/src/index.ts +0 -3
  81. package/tsconfig.json +0 -23
@@ -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
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Utility exports
3
+ */
4
+ export { getTenantIds, resolveApiKey } from "./tenant.ts";
5
+ export { generateCmsRouteReplacements, generateCollectionAndSingletonSlugRouteMap, } from "./route-map.ts";