@codenotch/codenotch.react 1.0.81

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 (39) hide show
  1. package/README.md +145 -0
  2. package/dist/components/Auml.d.ts +46 -0
  3. package/dist/components/Auml.d.ts.map +1 -0
  4. package/dist/components/Auml.js +208 -0
  5. package/dist/components/CodeEditor.d.ts +39 -0
  6. package/dist/components/CodeEditor.d.ts.map +1 -0
  7. package/dist/components/CodeEditor.js +174 -0
  8. package/dist/core/ProcessUtils.d.ts +10 -0
  9. package/dist/core/ProcessUtils.d.ts.map +1 -0
  10. package/dist/core/ProcessUtils.js +65 -0
  11. package/dist/core/SignalR.d.ts +30 -0
  12. package/dist/core/SignalR.d.ts.map +1 -0
  13. package/dist/core/SignalR.js +245 -0
  14. package/dist/index.d.ts +60 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +370 -0
  17. package/dist/models/AppManifestModels.d.ts +46 -0
  18. package/dist/models/AppManifestModels.d.ts.map +1 -0
  19. package/dist/models/AppManifestModels.js +2 -0
  20. package/dist/models/Codenotch.d.ts +256 -0
  21. package/dist/models/Codenotch.d.ts.map +1 -0
  22. package/dist/models/Codenotch.js +2 -0
  23. package/dist/models/Misc.d.ts +28 -0
  24. package/dist/models/Misc.d.ts.map +1 -0
  25. package/dist/models/Misc.js +2 -0
  26. package/dist/models/ProjectManifestModels.d.ts +126 -0
  27. package/dist/models/ProjectManifestModels.d.ts.map +1 -0
  28. package/dist/models/ProjectManifestModels.js +158 -0
  29. package/dist/utils/I18nUtils.d.ts +7 -0
  30. package/dist/utils/I18nUtils.d.ts.map +1 -0
  31. package/dist/utils/I18nUtils.js +37 -0
  32. package/package.json +44 -0
  33. package/src/components/CodeEditor.tsx +203 -0
  34. package/src/core/ProcessUtils.ts +86 -0
  35. package/src/core/SignalR.ts +375 -0
  36. package/src/index.ts +387 -0
  37. package/src/models/AppManifestModels.ts +54 -0
  38. package/src/models/Codenotch.ts +285 -0
  39. package/src/models/Misc.ts +32 -0
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const uuid_1 = require("uuid");
4
+ /**
5
+ * static class mainly containing a method to start processes
6
+ */
7
+ class ProcessUtils {
8
+ static async startProcess(subscribeFunc, clusterUrl, tenantName, projectName, processId, processInput, processInstanceId, startNodeId, token) {
9
+ let instanceId = processInstanceId ? processInstanceId : (0, uuid_1.v4)();
10
+ return new Promise(async (resolve, reject) => {
11
+ try {
12
+ // Another class/component is responsible for listening to processes' states and calling back when they finish
13
+ await subscribeFunc(instanceId, {
14
+ onCallback: (callback) => {
15
+ //TODO check redirect ?
16
+ },
17
+ onOver: (processResult) => {
18
+ // The process has finished
19
+ resolve(processResult);
20
+ },
21
+ });
22
+ // Create a new process instance
23
+ await ProcessUtils.launchMainProcess(clusterUrl, tenantName, projectName, processId, processInput, instanceId, startNodeId, token);
24
+ }
25
+ catch (err) {
26
+ console.error(err);
27
+ let processResult = {
28
+ isError: true,
29
+ processInstanceId: instanceId,
30
+ output: {},
31
+ errorMessage: err.message
32
+ };
33
+ resolve(processResult);
34
+ }
35
+ });
36
+ }
37
+ static async launchMainProcess(clusterUrl, tenantName, projectName, processId, processInput, processInstanceId, startNodeId, token) {
38
+ console.log("launching main process");
39
+ let input = processInput ? processInput : {};
40
+ // We launch the process with an http request
41
+ let url = `${clusterUrl}/${projectName.toLowerCase()}/processes/${processId}/instances`;
42
+ let body = {
43
+ id: processInstanceId,
44
+ input: input,
45
+ startNode: startNodeId
46
+ };
47
+ let requestHeaders = new Headers();
48
+ requestHeaders.set('Content-Type', 'application/json');
49
+ if (token && token !== "") {
50
+ requestHeaders.set(`${tenantName}AccessToken`, token);
51
+ }
52
+ const response = await fetch(url, {
53
+ method: 'POST',
54
+ credentials: 'include', // forward auth cookiesbody
55
+ body: JSON.stringify(body),
56
+ headers: requestHeaders
57
+ });
58
+ if (!response.ok) {
59
+ // Something went wrong
60
+ let errorMessage = await response.text();
61
+ throw new Error(errorMessage);
62
+ }
63
+ }
64
+ }
65
+ exports.default = ProcessUtils;
@@ -0,0 +1,30 @@
1
+ import { IProcessCallbacks, ICodenotchSignal } from "../models/Misc";
2
+ export declare class SignalR {
3
+ private _connection;
4
+ private _hubUrl;
5
+ private _accessToken;
6
+ private _signalCallbacks;
7
+ private _processCallbacks;
8
+ private _inactivityTimer;
9
+ private _unsubscribeTimers;
10
+ private _isConnecting;
11
+ private _pendingConnectionPromises;
12
+ private _keepAlive;
13
+ constructor(clusterUrl: string, serviceName: string, keepAlive: boolean, accessToken?: string);
14
+ connect(): Promise<void>;
15
+ private resolvePendingRequests;
16
+ private rejectPendingRequests;
17
+ private ensureConnected;
18
+ private onSignalReceived;
19
+ private getSignalPaths;
20
+ private onProcessCallbackReceived;
21
+ private onProcessOverReceived;
22
+ subscribeToProcessInstance(processInstanceId: string, callbacks: IProcessCallbacks): Promise<void>;
23
+ subscribeToSignal(signalId: string, callback: (signal: ICodenotchSignal) => void): Promise<() => Promise<void>>;
24
+ unsubscribeFromSignal(signalId: string, subscriptionId: string): Promise<void>;
25
+ private unsubscribeFromServerIfNoMoreSubscriptions;
26
+ private unsubscribeSignalServer;
27
+ private disconnectIfInactive;
28
+ disconnect(): void;
29
+ }
30
+ //# sourceMappingURL=SignalR.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SignalR.d.ts","sourceRoot":"","sources":["../../src/core/SignalR.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAKrE,qBAAa,OAAO;IAEhB,OAAO,CAAC,WAAW,CAA+B;IAClD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,YAAY,CAAqB;IAIzC,OAAO,CAAC,gBAAgB,CAAwF;IAGhH,OAAO,CAAC,iBAAiB,CAAmD;IAE5E,OAAO,CAAC,gBAAgB,CAAwB;IAChD,OAAO,CAAC,kBAAkB,CAA4C;IAEtE,OAAO,CAAC,aAAa,CAAkB;IACvC,OAAO,CAAC,0BAA0B,CAAsE;IAGxG,OAAO,CAAC,UAAU,CAAU;gBAET,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,CAAC,EAAE,MAAM;IAY9F,OAAO;IAmEb,OAAO,CAAC,sBAAsB;IAa9B,OAAO,CAAC,qBAAqB;YAaf,eAAe;IA6B7B,OAAO,CAAC,gBAAgB;IAqCxB,OAAO,CAAC,cAAc;IAsBtB,OAAO,CAAC,yBAAyB;IASjC,OAAO,CAAC,qBAAqB;IAcvB,0BAA0B,CAAC,iBAAiB,EAAE,MAAM,EAAE,SAAS,EAAE,iBAAiB;IAuBlF,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,GAAG,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IA4C/G,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM;IAkBpE,OAAO,CAAC,0CAA0C;YAYpC,uBAAuB;IAOrC,OAAO,CAAC,oBAAoB;IAarB,UAAU,IAAI,IAAI;CAY5B"}
@@ -0,0 +1,245 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SignalR = void 0;
4
+ const signalr_1 = require("@microsoft/signalr");
5
+ const uuid_1 = require("uuid");
6
+ class SignalR {
7
+ constructor(clusterUrl, serviceName, keepAlive, accessToken) {
8
+ this._unsubscribeTimers = {};
9
+ this._isConnecting = false;
10
+ this._pendingConnectionPromises = [];
11
+ this._hubUrl = `${clusterUrl}/${serviceName}/exec`;
12
+ this._accessToken = accessToken;
13
+ this._connection = null;
14
+ this._signalCallbacks = {};
15
+ this._processCallbacks = {};
16
+ this._inactivityTimer = null;
17
+ this._keepAlive = keepAlive;
18
+ }
19
+ async connect() {
20
+ console.log(`SignalR establishing connection...`);
21
+ this._isConnecting = true;
22
+ // Here we inject the access token if defined
23
+ let options = this._accessToken ? { accessTokenFactory: () => this._accessToken } : {};
24
+ // Skip negotiation and only use websockets
25
+ // This allows us to have multiple SignalR server without sticky sessions
26
+ // But it won't work in the rare cases where websockets are not supported
27
+ options.skipNegotiation = true;
28
+ options.transport = signalr_1.HttpTransportType.WebSockets;
29
+ //TODO if the server has multi instances, and the one we are connected fails, do sticky sessions prevent us from connecting to a healthy instance ?
30
+ this._connection = new signalr_1.HubConnectionBuilder()
31
+ .withUrl(this._hubUrl, options)
32
+ .withAutomaticReconnect([0, 1000, 3000, 5000, 10000, 30000, 60000, 90000]) // If the server is a single instance and decide to change node, it might take a little while
33
+ .configureLogging(signalr_1.LogLevel.Error)
34
+ .build();
35
+ this._connection.off("ReceiveSignal");
36
+ this._connection.off("ReceiveLog");
37
+ this._connection.off("ReceiveCallback");
38
+ this._connection.off("ReceiveProcessOver");
39
+ // On Signal
40
+ this._connection.on("ReceiveSignal", (signal) => this.onSignalReceived(signal));
41
+ // On log
42
+ this._connection.on("ReceiveLog", (log) => {
43
+ console.log(log);
44
+ });
45
+ // On callback received from process
46
+ //TODO only used to set wec content and redirect, do we still need those ?
47
+ this._connection.on("ReceiveCallback", (callback) => this.onProcessCallbackReceived(callback));
48
+ // On process completed
49
+ this._connection.on("ReceiveProcessOver", (processResult) => this.onProcessOverReceived(processResult));
50
+ this._connection.onclose((error) => console.log(`disconnected: ${error ? error.message : "no error"}`));
51
+ this._connection.onreconnected(() => this.resolvePendingRequests());
52
+ try {
53
+ await this._connection.start();
54
+ console.log(`SignalR connection established`);
55
+ this.resolvePendingRequests();
56
+ this._isConnecting = false;
57
+ }
58
+ catch (err) {
59
+ console.log(`SignalR connection error: ${err}`);
60
+ this.rejectPendingRequests(err);
61
+ this._isConnecting = false;
62
+ }
63
+ }
64
+ resolvePendingRequests() {
65
+ if (this._pendingConnectionPromises.length > 0) {
66
+ console.log(`Resolving ${this._pendingConnectionPromises.length} pending connection promises`);
67
+ }
68
+ for (let p of this._pendingConnectionPromises) {
69
+ p?.resolve();
70
+ }
71
+ }
72
+ rejectPendingRequests(error) {
73
+ if (this._pendingConnectionPromises.length > 0) {
74
+ console.log(`Rejecting ${this._pendingConnectionPromises.length} pending connection promises`);
75
+ }
76
+ for (let p of this._pendingConnectionPromises) {
77
+ p?.reject(error);
78
+ }
79
+ }
80
+ async ensureConnected() {
81
+ if (this._inactivityTimer) {
82
+ // cancel disconnection
83
+ clearTimeout(this._inactivityTimer);
84
+ this._inactivityTimer = null;
85
+ }
86
+ if (this._connection && this._connection.state === signalr_1.HubConnectionState.Connected) {
87
+ return Promise.resolve();
88
+ }
89
+ console.log(`SignalR not currently connected, waiting on connection...`);
90
+ const connectionPromise = new Promise((resolve, reject) => {
91
+ this._pendingConnectionPromises.push({ resolve, reject });
92
+ });
93
+ if (!this._isConnecting) {
94
+ this.connect();
95
+ }
96
+ // Return a promise that will resolve when the connection is established
97
+ return connectionPromise;
98
+ }
99
+ onSignalReceived(signal) {
100
+ try {
101
+ console.log(`Received signal '${signal.signalId}'`);
102
+ console.log(signal);
103
+ // parse the eventual data in the signal
104
+ if (signal.data) {
105
+ try {
106
+ signal.data = JSON.parse(signal.data);
107
+ }
108
+ catch {
109
+ // Data is not json but might still be valid
110
+ }
111
+ }
112
+ // If signal id contains multiple paths, trigger each one
113
+ var signalPaths = this.getSignalPaths(signal.signalId);
114
+ for (let p of signalPaths) {
115
+ if (this._signalCallbacks[p]) {
116
+ for (let callback of Object.values(this._signalCallbacks[p])) {
117
+ callback(signal);
118
+ }
119
+ }
120
+ }
121
+ }
122
+ catch (ex) {
123
+ console.log(`An exception occured during 'ReceiveSignal' callback:`);
124
+ console.log(ex);
125
+ }
126
+ }
127
+ getSignalPaths(signalId) {
128
+ // Signals can be segmented using '.', each segment corresponds to a more specific subject
129
+ // Subscribers can subscribe to a very specific signal or to a more general topic
130
+ // eg. Signal ref : invoice.update.0000-1111-2222-3333, will be received by subscribers on:
131
+ // -> 'invoice'
132
+ // -> 'invoice.update'
133
+ // -> 'invoice.update.0000-1111-2222-3333'
134
+ var signalPaths = [];
135
+ if (!signalId || signalId === '')
136
+ return signalPaths;
137
+ var segments = signalId.split('.');
138
+ for (let i = 0; i < segments.length; i++) {
139
+ signalPaths.push(segments.slice(0, i + 1).join('.')); // "path" from index 0 to i
140
+ }
141
+ return signalPaths;
142
+ }
143
+ onProcessCallbackReceived(callback) {
144
+ // Trigger the corresponding callback (set in ProcessUtils before launching the process)
145
+ if (this._processCallbacks[callback.processInstanceId]) {
146
+ this._processCallbacks[callback.processInstanceId].onCallback(callback);
147
+ }
148
+ }
149
+ onProcessOverReceived(processResult) {
150
+ // Trigger the corresponding callback (set in ProcessUtils before launching the process)
151
+ if (this._processCallbacks[processResult.processInstanceId]) {
152
+ this._processCallbacks[processResult.processInstanceId].onOver(processResult);
153
+ // Clean it, now that the process is over we should not receive anymore callbacks
154
+ delete this._processCallbacks[processResult.processInstanceId];
155
+ this.disconnectIfInactive();
156
+ }
157
+ }
158
+ async subscribeToProcessInstance(processInstanceId, callbacks) {
159
+ if (this._processCallbacks[processInstanceId]) {
160
+ // Already subscribed
161
+ return;
162
+ }
163
+ await this.ensureConnected();
164
+ try {
165
+ await this._connection.invoke("SubscribeToInstanceEvents", processInstanceId);
166
+ // Setup callbacks
167
+ this._processCallbacks[processInstanceId] = callbacks;
168
+ }
169
+ catch (err) {
170
+ console.error(`Couldn't subscribe to process '${processInstanceId}', : ${err}`);
171
+ }
172
+ }
173
+ async subscribeToSignal(signalId, callback) {
174
+ console.log(`subscribeToSignal: signalId:${signalId}`);
175
+ if (!signalId || signalId === "")
176
+ return () => Promise.resolve();
177
+ await this.ensureConnected();
178
+ if (this._unsubscribeTimers[signalId]) {
179
+ // cancel eventual unsubscription
180
+ clearTimeout(this._unsubscribeTimers[signalId]);
181
+ delete this._unsubscribeTimers[signalId];
182
+ }
183
+ if (!this._signalCallbacks[signalId]) {
184
+ // Create the subscription
185
+ try {
186
+ await this._connection.invoke("SubscribeToSignal", signalId);
187
+ // Remember which signals we are subscribed so we don't subscribe twice
188
+ if (!this._signalCallbacks[signalId]) {
189
+ this._signalCallbacks[signalId] = {};
190
+ }
191
+ }
192
+ catch (err) {
193
+ throw `Couldn't subscribe to signal '${signalId}', : ${err}`;
194
+ }
195
+ }
196
+ // Generate a new id to keep track of each subscription
197
+ let subscriptionId = (0, uuid_1.v4)();
198
+ this._signalCallbacks[signalId][subscriptionId] = callback;
199
+ // Return the unsubscribe function
200
+ return async () => await this.unsubscribeFromSignal(signalId, subscriptionId);
201
+ }
202
+ async unsubscribeFromSignal(signalId, subscriptionId) {
203
+ console.log(`unsubscribeFromSignal: signalId:${signalId}, subscriptionId:${subscriptionId}`);
204
+ if (!this._signalCallbacks[signalId]) {
205
+ return;
206
+ }
207
+ delete this._signalCallbacks[signalId][subscriptionId];
208
+ // From this point we won't send events to the component who just unsubscribed, but we are still subscribed to the signal on the server so we'll recieve new signals
209
+ this.unsubscribeFromServerIfNoMoreSubscriptions(signalId);
210
+ // If no more subscriptions, we can disconnect from the server
211
+ this.disconnectIfInactive();
212
+ }
213
+ unsubscribeFromServerIfNoMoreSubscriptions(signalId) {
214
+ if (Object.keys(this._signalCallbacks[signalId]).length === 0) {
215
+ delete this._signalCallbacks[signalId];
216
+ // No more subscriptions for this signal, we can unsubscribe from the server
217
+ // but in order to not send too many requests, we will wait a little bit before actually unsubscribing, the client might come back to the tab that need this signal in a few seconds
218
+ this._unsubscribeTimers[signalId] = setTimeout(() => this.unsubscribeSignalServer(signalId), 1 * 60 * 1000);
219
+ }
220
+ }
221
+ async unsubscribeSignalServer(signalId) {
222
+ console.log(`unsubscribeSignalServer: signalId:${signalId}`);
223
+ await this._connection.invoke("UnsubscribeFromSignal", signalId);
224
+ }
225
+ disconnectIfInactive() {
226
+ if (this._keepAlive)
227
+ return;
228
+ if (Object.keys(this._signalCallbacks).length === 0 && Object.keys(this._processCallbacks).length === 0) {
229
+ // No more subscriptions, disconnect from the server after a little while (5 minutes) to save resources
230
+ this._inactivityTimer = setTimeout(() => this.disconnect(), 5 * 60 * 1000);
231
+ console.log("SignalR has no more active subscriptions, will disconnect in 5 minutes...");
232
+ }
233
+ }
234
+ disconnect() {
235
+ if (this._connection) {
236
+ this._connection.off("ReceiveCallback");
237
+ this._connection.off("ReceiveProcessOver");
238
+ this._connection.off("ReceiveSignal");
239
+ this._connection.off("ReceiveLog");
240
+ this._connection.stop();
241
+ this._connection = null;
242
+ }
243
+ }
244
+ }
245
+ exports.SignalR = SignalR;
@@ -0,0 +1,60 @@
1
+ import { ICodenotchApi, ICodenotchEnv } from "./models/Codenotch";
2
+ import CodeEditor from "./components/CodeEditor";
3
+ /**
4
+ * The Codenotch runtime environment, populated by {@link init}.
5
+ * Prefer reading it through `useCodenotch().env`.
6
+ */
7
+ declare const env: ICodenotchEnv;
8
+ /**
9
+ * Initialize the Codenotch environment from the given key-value pairs.
10
+ *
11
+ * This is normally called once, by the page hosting the application, with the
12
+ * environment variables injected by the Codenotch runtime (URL parameters or
13
+ * any other source). It must be called before `useCodenotch()` is used.
14
+ *
15
+ * Recognized key formats:
16
+ * - `i18n.<key>.<language>` — a translation, stored in `env.i18n[language][key]`.
17
+ * The first language found becomes the current language if none is set.
18
+ * - `GLOBAL.<name>` — JSON value assigned to `window[name]` (or `globalThis`).
19
+ * - `appManifest` / `projectManifest` — parsed as JSON into the environment.
20
+ * - anything else — copied as-is onto {@link env}.
21
+ *
22
+ * The UI theme is resolved from the `_theme` URL parameter, or from
23
+ * `prefers-color-scheme` as a fallback.
24
+ *
25
+ * @param envVariables An object containing the Codenotch environment variables as key-value pairs.
26
+ * @example
27
+ * import { init } from 'codenotch-react';
28
+ * init({
29
+ * clusterUrl: 'https://cluster.example.com',
30
+ * serviceName: 'myproject',
31
+ * tenantName: 'acme',
32
+ * 'i18n.welcome.en': 'Welcome',
33
+ * 'i18n.welcome.fr': 'Bienvenue'
34
+ * });
35
+ */
36
+ declare function init(envVariables: any): void;
37
+ /**
38
+ * Return the Codenotch client API bound to the current environment.
39
+ *
40
+ * Despite its name this is NOT a React hook — it is a plain function with no
41
+ * hook rules attached: it can be called anywhere (components, handlers, plain
42
+ * modules). `init()` must have been called first, which the Codenotch runtime
43
+ * does automatically when serving the application.
44
+ *
45
+ * @returns The Codenotch API: BPMN processes, SioQL queries, i18n, signals, theme…
46
+ * @example
47
+ * import { useCodenotch } from 'codenotch-react';
48
+ *
49
+ * const MyApp: React.FC = () => {
50
+ * const cn = useCodenotch();
51
+ * return <h1>{cn.i18n('welcome')}</h1>;
52
+ * };
53
+ */
54
+ declare function useCodenotch(): ICodenotchApi;
55
+ export { env, useCodenotch, init, CodeEditor };
56
+ export * from "./models/Codenotch";
57
+ export * from "./models/Misc";
58
+ export * from "./models/AppManifestModels";
59
+ export * from "@codenotch/codenotch.core";
60
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAoB,aAAa,EAAkB,MAAM,oBAAoB,CAAC;AAEpG,OAAO,UAAU,MAAM,yBAAyB,CAAC;AAIjD;;;GAGG;AACH,QAAA,MAAM,GAAG,EAAE,aAAkB,CAAC;AAM9B;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,iBAAS,IAAI,CAAC,YAAY,EAAE,GAAG,GAAG,IAAI,CAyFrC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,iBAAS,YAAY,IAAI,aAAa,CAsNrC;AAED,OAAO,EACH,GAAG,EACH,YAAY,EACZ,IAAI,EACJ,UAAU,EACb,CAAC;AAMF,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,4BAA4B,CAAC;AAC3C,cAAc,2BAA2B,CAAC"}