@contello/sdk-client 8.21.0 → 8.21.2

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 ADDED
@@ -0,0 +1,124 @@
1
+ # @contello/sdk-client
2
+
3
+ GraphQL SDK client for Contello CMS with WebSocket transport and connection pooling.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @contello/sdk-client rxjs graphql graphql-ws
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### Basic setup
14
+
15
+ ```ts
16
+ import { ContelloSdkClient } from '@contello/sdk-client';
17
+ import { getSdk } from './generated-sdk';
18
+
19
+ const client = new ContelloSdkClient(getSdk, {
20
+ url: 'https://example.com',
21
+ project: 'myProjectId',
22
+ token: 'app-a123b456c7890d123456789',
23
+ });
24
+
25
+ await client.connect();
26
+
27
+ const result = await client.sdk.someQuery({ id: '123' });
28
+ ```
29
+
30
+ ### Connection pooling
31
+
32
+ By default, the client maintains a pool of 5 WebSocket connections and round-robins requests across them.
33
+
34
+ ```ts
35
+ const client = new ContelloSdkClient(getSdk, {
36
+ url: 'https://example.com',
37
+ project: 'myProjectId',
38
+ token: 'token',
39
+ pooling: {
40
+ size: 10,
41
+ },
42
+ });
43
+ ```
44
+
45
+ To disable pooling:
46
+
47
+ ```ts
48
+ pooling: {
49
+ enabled: false;
50
+ }
51
+ ```
52
+
53
+ ### Middleware
54
+
55
+ Middlewares can intercept requests and outgoing WebSocket messages.
56
+
57
+ ```ts
58
+ import type { ContelloSdkClientMiddleware } from '@contello/sdk-client';
59
+
60
+ const loggingMiddleware: ContelloSdkClientMiddleware = {
61
+ onRequest(request, next) {
62
+ console.log(`${request.kind}: ${request.operationName}`);
63
+
64
+ return next();
65
+ },
66
+ };
67
+
68
+ const client = new ContelloSdkClient(getSdk, {
69
+ url: 'https://example.com',
70
+ project: 'myProjectId',
71
+ token: 'token',
72
+ middlewares: [loggingMiddleware],
73
+ });
74
+ ```
75
+
76
+ ### Client events
77
+
78
+ ```ts
79
+ const client = new ContelloSdkClient(getSdk, {
80
+ url: 'https://example.com',
81
+ project: 'myProjectId',
82
+ token: 'token',
83
+ client: {
84
+ retryAttempts: 5,
85
+ onConnected: (ctx) => console.log(`connected: ${ctx.connectionId}`),
86
+ onError: (ctx, error) => console.error(`error on ${ctx.connectionId}:`, error),
87
+ onClosed: (ctx) => console.log(`closed: ${ctx.connectionId}`),
88
+ },
89
+ });
90
+ ```
91
+
92
+ ### Disconnect
93
+
94
+ ```ts
95
+ await client.disconnect();
96
+ ```
97
+
98
+ > `connect()` resolves once all WebSocket connections in the pool have been acknowledged by the server. The underlying transport retries automatically on transient failures, so the promise will stay pending until every connection succeeds. If retries are exhausted the promise will remain unresolved.
99
+
100
+ ## API
101
+
102
+ ### `ContelloSdkClient`
103
+
104
+ | Option | Type | Default | Description |
105
+ | ---------------------- | ------------------------------- | ------- | ------------------------------------ |
106
+ | `url` | `string` | — | Base URL of the Contello server |
107
+ | `project` | `string` | — | Project identifier |
108
+ | `token` | `string` | — | Authentication token |
109
+ | `middlewares` | `ContelloSdkClientMiddleware[]` | `[]` | Request/message middlewares |
110
+ | `pooling.enabled` | `boolean` | `true` | Enable connection pooling |
111
+ | `pooling.size` | `number` | `5` | Number of WebSocket connections |
112
+ | `client.retryAttempts` | `number` | `3` | Retry attempts on connection failure |
113
+
114
+ ### Methods
115
+
116
+ | Method | Returns | Description |
117
+ | -------------- | --------------- | -------------------------- |
118
+ | `sdk` | `T` | The generated SDK instance |
119
+ | `connect()` | `Promise<void>` | Open WebSocket connections |
120
+ | `disconnect()` | `Promise<void>` | Close all connections |
121
+
122
+ ## License
123
+
124
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,183 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ ContelloSdkClient: () => ContelloSdkClient
24
+ });
25
+ module.exports = __toCommonJS(index_exports);
26
+
27
+ // src/client.ts
28
+ var import_graphql_ws = require("graphql-ws");
29
+
30
+ // src/pool.ts
31
+ var ConnectionPool = class {
32
+ constructor(createClient2, poolSize) {
33
+ this.createClient = createClient2;
34
+ this.poolSize = poolSize;
35
+ }
36
+ clients = [];
37
+ currentIndex = 0;
38
+ async connect() {
39
+ const connected = [];
40
+ for (let i = 0; i < this.poolSize; i++) {
41
+ const client = this.createClient(`${i + 1}`);
42
+ this.clients.push(client);
43
+ connected.push(
44
+ new Promise((resolve) => {
45
+ const unsubscribe = client.on("connected", () => {
46
+ unsubscribe();
47
+ resolve();
48
+ });
49
+ })
50
+ );
51
+ }
52
+ await Promise.all(connected);
53
+ }
54
+ get() {
55
+ if (this.clients.length === 0) {
56
+ throw new Error("Connection pool is empty. Please call connect() first.");
57
+ }
58
+ const client = this.clients[this.currentIndex];
59
+ this.currentIndex = (this.currentIndex + 1) % this.poolSize;
60
+ if (!client) {
61
+ throw new Error("No available WebSocket client");
62
+ }
63
+ return client;
64
+ }
65
+ async disconnect() {
66
+ const closed = this.clients.map(
67
+ (client) => new Promise((resolve) => {
68
+ const unsubscribe = client.on("closed", () => {
69
+ unsubscribe();
70
+ resolve();
71
+ });
72
+ client.dispose();
73
+ })
74
+ );
75
+ this.clients = [];
76
+ await Promise.all(closed);
77
+ }
78
+ };
79
+
80
+ // src/sdk.ts
81
+ var import_rxjs = require("rxjs");
82
+ var createSdk = (client, middlewares, getSdk) => {
83
+ return getSdk((doc, vars, options) => {
84
+ if (options) {
85
+ console.warn("options are not supported yet");
86
+ }
87
+ const query = doc.loc?.source.body;
88
+ if (!query) {
89
+ throw new Error("No query provided");
90
+ }
91
+ if (vars && typeof vars !== "object") {
92
+ throw new Error("Variables must be an object");
93
+ }
94
+ const operationDef = doc.definitions.find((def) => def.kind === "OperationDefinition");
95
+ if (!operationDef) {
96
+ throw new Error("No operation definition found");
97
+ }
98
+ const kind = operationDef.operation;
99
+ const executeRequest = () => {
100
+ const wsClient = client();
101
+ return new import_rxjs.Observable((obs) => wsClient.subscribe({ query, variables: vars }, obs));
102
+ };
103
+ const request = {
104
+ kind,
105
+ operationName: operationDef.name?.value ?? "",
106
+ query,
107
+ variables: vars || {}
108
+ };
109
+ const executeWithMiddlewares = (middlewares2, index) => {
110
+ if (index >= middlewares2.length) {
111
+ return executeRequest();
112
+ }
113
+ const middleware = middlewares2[index];
114
+ if (middleware?.onRequest) {
115
+ return middleware.onRequest(request, () => executeWithMiddlewares(middlewares2, index + 1));
116
+ }
117
+ return executeWithMiddlewares(middlewares2, index + 1);
118
+ };
119
+ const res = executeWithMiddlewares(middlewares, 0);
120
+ return kind !== "subscription" ? (0, import_rxjs.firstValueFrom)(res) : res;
121
+ });
122
+ };
123
+
124
+ // src/client.ts
125
+ var ContelloSdkClient = class {
126
+ _pool;
127
+ _sdk;
128
+ constructor(getSdk, params) {
129
+ const { url, project, token, client, pooling } = params;
130
+ const websocketUrl = `${url}/graphql/projects/${project}`.replace(/^http/i, "ws");
131
+ this._pool = new ConnectionPool(
132
+ (id) => {
133
+ const context = Object.freeze({ connectionId: id, websocketUrl });
134
+ return (0, import_graphql_ws.createClient)({
135
+ url: websocketUrl,
136
+ connectionParams: { authorization: `Bearer ${token}` },
137
+ lazy: false,
138
+ keepAlive: 3e4,
139
+ retryAttempts: client?.retryAttempts ?? 3,
140
+ shouldRetry: () => true,
141
+ jsonMessageReplacer: (key, value) => {
142
+ if (!key) {
143
+ let message = value;
144
+ for (const middleware of params.middlewares ?? []) {
145
+ if (middleware.onOutgoingMessage) {
146
+ message = middleware.onOutgoingMessage(message);
147
+ }
148
+ }
149
+ return message;
150
+ }
151
+ return value;
152
+ },
153
+ ...client?.onError ? { onNonLazyError: (e) => client.onError(context, e) } : {},
154
+ on: {
155
+ ...client?.onError ? { error: (e) => client.onError(context, e) } : {},
156
+ ...client?.onConnected ? { connected: () => client.onConnected(context) } : {},
157
+ ...client?.onClosed ? { closed: () => client.onClosed(context) } : {},
158
+ ...client?.onConnecting ? { connecting: () => client.onConnecting(context) } : {},
159
+ ...client?.onOpened ? { opened: () => client.onOpened(context) } : {},
160
+ ...client?.onMessage ? { message: (m) => client.onMessage(context, m) } : {},
161
+ ...client?.onPing ? { ping: () => client.onPing(context) } : {},
162
+ ...client?.onPong ? { pong: () => client.onPong(context) } : {}
163
+ }
164
+ });
165
+ },
166
+ pooling?.enabled === false ? 1 : pooling?.size ?? 5
167
+ );
168
+ this._sdk = { sdk: createSdk(() => this._pool.get(), params.middlewares ?? [], getSdk) };
169
+ }
170
+ get sdk() {
171
+ return this._sdk.sdk;
172
+ }
173
+ async connect() {
174
+ await this._pool.connect();
175
+ }
176
+ async disconnect() {
177
+ await this._pool.disconnect();
178
+ }
179
+ };
180
+ // Annotate the CommonJS export names for ESM import in node:
181
+ 0 && (module.exports = {
182
+ ContelloSdkClient
183
+ });
@@ -0,0 +1,50 @@
1
+ import { Observable } from 'rxjs';
2
+ import { DocumentNode, ExecutionResult } from 'graphql';
3
+
4
+ type ContelloSdkClientMiddleware = {
5
+ onRequest?: (request: {
6
+ kind: 'query' | 'mutation' | 'subscription';
7
+ operationName: string;
8
+ query: string;
9
+ variables: Record<string, any>;
10
+ }, next: () => Observable<any>) => Observable<any>;
11
+ onOutgoingMessage?: (message: any) => any;
12
+ };
13
+
14
+ type Requester<C = any, E = unknown> = <R, V>(doc: DocumentNode, vars?: V, options?: C) => Promise<ExecutionResult<R, E>> | Observable<ExecutionResult<R, E>>;
15
+
16
+ type ClientEventContext = {
17
+ connectionId: string;
18
+ websocketUrl: string;
19
+ };
20
+ type ContelloSdkClientParams = {
21
+ url: string;
22
+ project: string;
23
+ token: string;
24
+ middlewares?: ContelloSdkClientMiddleware[] | undefined;
25
+ pooling?: {
26
+ enabled?: boolean | undefined;
27
+ size?: number | undefined;
28
+ } | undefined;
29
+ client?: {
30
+ retryAttempts?: number | undefined;
31
+ onError?: (context: ClientEventContext, error: unknown) => void;
32
+ onConnected?: (context: ClientEventContext) => void;
33
+ onClosed?: (context: ClientEventContext) => void;
34
+ onConnecting?: (context: ClientEventContext) => void;
35
+ onOpened?: (context: ClientEventContext) => void;
36
+ onMessage?: (context: ClientEventContext, message: any) => void;
37
+ onPing?: (context: ClientEventContext) => void;
38
+ onPong?: (context: ClientEventContext) => void;
39
+ } | undefined;
40
+ };
41
+ declare class ContelloSdkClient<T> {
42
+ private _pool;
43
+ private _sdk;
44
+ constructor(getSdk: <C, E>(requester: Requester<C, E>) => T, params: ContelloSdkClientParams);
45
+ get sdk(): T;
46
+ connect(): Promise<void>;
47
+ disconnect(): Promise<void>;
48
+ }
49
+
50
+ export { ContelloSdkClient, type ContelloSdkClientMiddleware, type ContelloSdkClientParams };
package/dist/index.d.ts CHANGED
@@ -1,53 +1,50 @@
1
- import { DocumentNode } from 'graphql';
2
- import { ExecutionResult } from 'graphql';
3
- import { Observable } from 'rxjs';
4
-
5
- declare type ClientEventContext = {
6
- connectionId: string;
7
- websocketUrl: string;
8
- };
9
-
10
- export declare class ContelloSdkClient<T> {
11
- private _pool;
12
- private _sdk;
13
- constructor(getSdk: <C, E>(requester: Requester<C, E>) => T, params: ContelloSdkClientParams);
14
- get sdk(): T;
15
- connect(): Promise<void>;
16
- disconnect(): Promise<void>;
17
- }
18
-
19
- export declare type ContelloSdkClientMiddleware = {
20
- onRequest?: (request: {
21
- kind: 'query' | 'mutation' | 'subscription';
22
- operationName: string;
23
- query: string;
24
- variables: Record<string, any>;
25
- }, next: () => Observable<any>) => Observable<any>;
26
- onOutgoingMessage?: (message: any) => any;
27
- };
28
-
29
- export declare type ContelloSdkClientParams = {
30
- url: string;
31
- project: string;
32
- token: string;
33
- middlewares?: ContelloSdkClientMiddleware[] | undefined;
34
- pooling?: {
35
- enabled?: boolean | undefined;
36
- size?: number | undefined;
37
- } | undefined;
38
- client?: {
39
- retryAttempts?: number | undefined;
40
- onError?: (context: ClientEventContext, error: unknown) => void;
41
- onConnected?: (context: ClientEventContext) => void;
42
- onClosed?: (context: ClientEventContext) => void;
43
- onConnecting?: (context: ClientEventContext) => void;
44
- onOpened?: (context: ClientEventContext) => void;
45
- onMessage?: (context: ClientEventContext, message: any) => void;
46
- onPing?: (context: ClientEventContext) => void;
47
- onPong?: (context: ClientEventContext) => void;
48
- } | undefined;
49
- };
50
-
51
- declare type Requester<C = any, E = unknown> = <R, V>(doc: DocumentNode, vars?: V, options?: C) => Promise<ExecutionResult<R, E>> | Observable<ExecutionResult<R, E>>;
52
-
53
- export { }
1
+ import { Observable } from 'rxjs';
2
+ import { DocumentNode, ExecutionResult } from 'graphql';
3
+
4
+ type ContelloSdkClientMiddleware = {
5
+ onRequest?: (request: {
6
+ kind: 'query' | 'mutation' | 'subscription';
7
+ operationName: string;
8
+ query: string;
9
+ variables: Record<string, any>;
10
+ }, next: () => Observable<any>) => Observable<any>;
11
+ onOutgoingMessage?: (message: any) => any;
12
+ };
13
+
14
+ type Requester<C = any, E = unknown> = <R, V>(doc: DocumentNode, vars?: V, options?: C) => Promise<ExecutionResult<R, E>> | Observable<ExecutionResult<R, E>>;
15
+
16
+ type ClientEventContext = {
17
+ connectionId: string;
18
+ websocketUrl: string;
19
+ };
20
+ type ContelloSdkClientParams = {
21
+ url: string;
22
+ project: string;
23
+ token: string;
24
+ middlewares?: ContelloSdkClientMiddleware[] | undefined;
25
+ pooling?: {
26
+ enabled?: boolean | undefined;
27
+ size?: number | undefined;
28
+ } | undefined;
29
+ client?: {
30
+ retryAttempts?: number | undefined;
31
+ onError?: (context: ClientEventContext, error: unknown) => void;
32
+ onConnected?: (context: ClientEventContext) => void;
33
+ onClosed?: (context: ClientEventContext) => void;
34
+ onConnecting?: (context: ClientEventContext) => void;
35
+ onOpened?: (context: ClientEventContext) => void;
36
+ onMessage?: (context: ClientEventContext, message: any) => void;
37
+ onPing?: (context: ClientEventContext) => void;
38
+ onPong?: (context: ClientEventContext) => void;
39
+ } | undefined;
40
+ };
41
+ declare class ContelloSdkClient<T> {
42
+ private _pool;
43
+ private _sdk;
44
+ constructor(getSdk: <C, E>(requester: Requester<C, E>) => T, params: ContelloSdkClientParams);
45
+ get sdk(): T;
46
+ connect(): Promise<void>;
47
+ disconnect(): Promise<void>;
48
+ }
49
+
50
+ export { ContelloSdkClient, type ContelloSdkClientMiddleware, type ContelloSdkClientParams };