@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
package/README.md ADDED
@@ -0,0 +1,145 @@
1
+ # codenotch-react
2
+
3
+ React bindings for [Codenotch](https://codenotch.com) applications.
4
+
5
+ Codenotch is a full-stack development platform: a project combines server-side **BPMN processes**, **SQL tables** / **NoSQL documents**, **i18n** translations and **React** apps. This package is the client-side bridge between those React apps and the Codenotch runtime: it lets a component start BPMN processes, run SioQL queries, translate i18n keys, listen to real-time signals, and manage theme/language.
6
+
7
+ > **Requirements** — Codenotch apps run on **React 16** (`react@^16.14.0`). Do not use React 17/18 APIs (`createRoot`, `useId`, automatic JSX runtime…); hooks work fine.
8
+
9
+ ## Quick start
10
+
11
+ ```tsx
12
+ import React from 'react';
13
+ import { useCodenotch } from 'codenotch-react';
14
+
15
+ const MyApp: React.FC = () => {
16
+ const cn = useCodenotch();
17
+
18
+ return <div className="p-4">
19
+ <h1 className="text-xl font-bold">{cn.i18n('welcome')}</h1>
20
+ </div>;
21
+ };
22
+
23
+ export default MyApp;
24
+ ```
25
+
26
+ `useCodenotch()` is **not a React hook** despite its name: it is a plain function that returns the API object bound to the current environment. It can be called anywhere — components, event handlers, plain modules.
27
+
28
+ The environment (cluster URL, service name, language, translations…) is set up by `init(envVariables)`, which the Codenotch runtime calls automatically from the page hosting the application. You only call `init` yourself in custom hosting scenarios.
29
+
30
+ ## API overview
31
+
32
+ | Member | Description |
33
+ |--------|-------------|
34
+ | `cn.env` | Readonly environment: `clusterUrl`, `serviceName`, `tenantName`, `accessToken`, `language`, `theme`, `i18n`, `appManifest`, `projectManifest`. |
35
+ | `cn.i18n(key, ...args)` | Translate a key for the current language, filling `{0}`, `{1}`, … placeholders. |
36
+ | `cn.startProcess(name, startNodeId, inputs)` | Start a server-side BPMN process and await its result. |
37
+ | `cn.requestSioql(sioql, verbose?)` | Run a SioQL query (XML, SELECT-only) against the project's tables. |
38
+ | `cn.listenSignal(signalId, callback)` | Subscribe to real-time signals emitted by BPMN processes. |
39
+ | `cn.showDialog(jsx)` | Render a JSX element in a fullscreen modal dialog. |
40
+ | `cn.setTheme(t)` / `cn.getTheme()` | `'light' \| 'dark'`; `setTheme` also toggles the class on `<html>` (Tailwind `dark:`). |
41
+ | `cn.setLanguage(l)` / `cn.getLanguage()` / `cn.getLanguages()` | Current language and the languages declared in `manifest.json`. |
42
+ | `cn.getProjectFile(path)` / `cn.getProjectFileUrl(path)` | Read/link a file of the deployed project. |
43
+ | `cn.getUrlParams()` | Query-string parameters of the current URL as a plain object. |
44
+ | `cn.uuid()` | Random UUID v4. |
45
+ | `cn.getProjectManifest()` / `cn.getAppManifest()` | Project / application manifests. |
46
+
47
+ Full signatures and JSDoc live in `dist/index.d.ts` (sources in `src/`).
48
+
49
+ ## Calling a BPMN process
50
+
51
+ `processName` is the `.bpmn` file name without extension; `startNodeId` is the id of the start event to trigger (conventionally `'start'`); `inputs` are the input parameters declared by that start event. The result's `output` contains the parameters of the end event reached.
52
+
53
+ ```tsx
54
+ const result = await cn.startProcess('getTodos', 'start', { UserId: userId });
55
+
56
+ if (!result.isError) {
57
+ setTodos(result.output.todos);
58
+ } else {
59
+ console.error(result.errorMessage);
60
+ }
61
+ ```
62
+
63
+ ## Querying tables with SioQL
64
+
65
+ SioQL is Codenotch's XML query language over the project's SQL tables (SELECT only — writes go through BPMN processes). The root `xmlns` must be the project's `serviceName`; each queried table's `Ref` attribute names its result set:
66
+
67
+ ```tsx
68
+ const data = await cn.requestSioql(`
69
+ <SioQL xmlns="myproject" PageSize="10" PageIndex="0">
70
+ <Users Ref="results">
71
+ <Id />
72
+ <Email />
73
+ <IsAdmin Equal="true" />
74
+ </Users>
75
+ </SioQL>`);
76
+
77
+ console.log(data.results); // [{ Id: '...', Email: '...' }, ...]
78
+ ```
79
+
80
+ ## Translations (i18n)
81
+
82
+ Translations come from the project's `.i18n.csv` files and are injected into `cn.env.i18n` at startup. `cn.i18n(key, ...args)` translates for the current language and replaces `{0}`, `{1}`, … placeholders; it never throws — an unknown key is returned as-is (with a console warning).
83
+
84
+ ```tsx
85
+ cn.i18n('welcome'); // "Bienvenue"
86
+ cn.i18n('greeting', 'Ada', 3); // "Bonjour Ada, 3 messages" (from "Bonjour {0}, {1} messages")
87
+ cn.setLanguage('en'); // switch language at runtime
88
+ ```
89
+
90
+ ## Real-time signals
91
+
92
+ BPMN processes can broadcast signals; subscribe from the UI with `listenSignal`:
93
+
94
+ ```tsx
95
+ useEffect(() => {
96
+ let sub: IDisposable | undefined;
97
+ cn.listenSignal('todosChanged', (signal) => refresh(signal.data))
98
+ .then(s => { sub = s; });
99
+ return () => sub?.dispose();
100
+ }, []);
101
+ ```
102
+
103
+ ## Dialogs
104
+
105
+ ```tsx
106
+ const dialog = cn.showDialog(
107
+ <div className="bg-white dark:bg-gray-800 p-6 rounded shadow">
108
+ <p>{cn.i18n('confirm.message')}</p>
109
+ <button onClick={() => dialog.close()}>{cn.i18n('close')}</button>
110
+ </div>
111
+ );
112
+ ```
113
+
114
+ The dialog stays open until you call `dialog.close()`.
115
+
116
+ ## Type-safe processes and translation keys
117
+
118
+ `startProcess` and `i18n` are typed through two registries, `ProcessRegistry` and `TranslationRegistry`, that the **Codenotch IDE fills by declaration merging**: it generates `typings/process.d.ts` and `typings/i18n.d.ts` in each project from the `.bpmn` and `.i18n.csv` files (never edit those files — refresh the project instead). The generated files look like:
119
+
120
+ ```ts
121
+ import 'codenotch-react';
122
+
123
+ declare module 'codenotch-react' {
124
+ interface TranslationRegistry {
125
+ 'welcome': [];
126
+ 'greeting': [arg1: any, arg2: any];
127
+ }
128
+
129
+ interface ProcessRegistry {
130
+ 'getTodos': {
131
+ nodes: { 'start': { UserId: string } };
132
+ output: { todos: any[] };
133
+ };
134
+ }
135
+ }
136
+ ```
137
+
138
+ With these in the compilation, keys, inputs and outputs are strictly checked and autocompleted. **Without them** (project compiled outside the IDE), the registries are empty and both methods gracefully fall back to plain `string` keys and untyped arguments — the code still compiles.
139
+
140
+ ## Notes for AI assistants
141
+
142
+ - The complete typed API surface is in `dist/index.d.ts`; the readable implementation ships in `src/` (entry point `src/index.ts`).
143
+ - `useCodenotch()` is a plain function, not a hook — no hook rules apply.
144
+ - Never hand-edit a project's `typings/i18n.d.ts` / `typings/process.d.ts`: they are regenerated by the Codenotch IDE.
145
+ - Codenotch apps are React 16 + Tailwind CSS; SioQL is SELECT-only (writes go through BPMN processes via `startProcess`).
@@ -0,0 +1,46 @@
1
+ import React from "react";
2
+ import { SpaRenderStatus } from '@echino/echino.ui.framework/components/SpaBuilder/common/ISpaRenderProps';
3
+ import type { ICodenotchEnv } from "../models/Codenotch";
4
+ interface IAumlProps {
5
+ /** The AUML (XML) application description to render. */
6
+ value: string;
7
+ /** When `true`, logs compilation and token-refresh details to the console. */
8
+ verbose?: boolean;
9
+ /** Codenotch environment; provides the i18n dictionaries injected into the AUML and the theme. */
10
+ env?: ICodenotchEnv;
11
+ }
12
+ interface IAumlState {
13
+ loaded: boolean;
14
+ inspectorEnabled: boolean;
15
+ }
16
+ /**
17
+ * Renders a legacy AUML (XML) application description inside a React app.
18
+ *
19
+ * Compiles the AUML with the environment's i18n variables, renders it through
20
+ * the Echino SPA renderer, shows a loading overlay until completed, and keeps
21
+ * the user's access token refreshed in the background.
22
+ *
23
+ * Relies on globals injected by the Codenotch runtime hosting page
24
+ * (`serviceName`, `tenant`, `user`, `languages`, `appManifest`…) — it is not
25
+ * usable outside a Codenotch-served application.
26
+ */
27
+ export declare class Auml extends React.Component<IAumlProps, IAumlState> {
28
+ _refreshTokenTimer: NodeJS.Timeout | undefined;
29
+ constructor(props: IAumlProps);
30
+ componentDidMount(): void;
31
+ registerRefreshToken(): void;
32
+ componentWillUnmount(): void;
33
+ progress(s: SpaRenderStatus): void;
34
+ retrieveInputs(): {
35
+ [k: string]: string;
36
+ };
37
+ getTimeToTokenExpiration(): number | null;
38
+ getTimeToNewTokenExpiration(expirationDateTime: string): number;
39
+ msToTime(ms: number): string;
40
+ parseJwt(token: string): any;
41
+ render(): React.JSX.Element;
42
+ renderLoadingContent(): React.JSX.Element;
43
+ refreshExpiredToken(): Promise<void>;
44
+ }
45
+ export {};
46
+ //# sourceMappingURL=Auml.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Auml.d.ts","sourceRoot":"","sources":["../../src/components/Auml.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,eAAe,EAAE,MAAM,0EAA0E,CAAC;AAE3G,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AASzD,UAAU,UAAU;IAChB,wDAAwD;IACxD,KAAK,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kGAAkG;IAClG,GAAG,CAAC,EAAE,aAAa,CAAC;CACvB;AAED,UAAU,UAAU;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,gBAAgB,EAAE,OAAO,CAAC;CAC7B;AAED;;;;;;;;;;GAUG;AACH,qBAAa,IAAK,SAAQ,KAAK,CAAC,SAAS,CAAC,UAAU,EAAE,UAAU,CAAC;IAE7D,kBAAkB,EAAE,MAAM,CAAC,OAAO,GAAG,SAAS,CAAC;gBAEnC,KAAK,EAAE,UAAU;IAa7B,iBAAiB;IAIjB,oBAAoB;IAoCpB,oBAAoB;IAOpB,QAAQ,CAAC,CAAC,EAAE,eAAe;IAS3B,cAAc;;;IAMd,wBAAwB,IAAI,MAAM,GAAG,IAAI;IAqCzC,2BAA2B,CAAC,kBAAkB,EAAE,MAAM,GAAG,MAAM;IAO/D,QAAQ,CAAC,EAAE,EAAE,MAAM;IAmBnB,QAAQ,CAAC,KAAK,EAAE,MAAM;IAUtB,MAAM;IAoDN,oBAAoB;IAkBd,mBAAmB;CAoC5B"}
@@ -0,0 +1,208 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.Auml = void 0;
7
+ const react_1 = __importDefault(require("react"));
8
+ const SpaRenderWithBrowserRouter_1 = require("@echino/echino.ui.framework/components/SpaBuilder/SpaRender/SpaRenderWithBrowserRouter");
9
+ const I18nUtils_1 = __importDefault(require("../utils/I18nUtils"));
10
+ /**
11
+ * Renders a legacy AUML (XML) application description inside a React app.
12
+ *
13
+ * Compiles the AUML with the environment's i18n variables, renders it through
14
+ * the Echino SPA renderer, shows a loading overlay until completed, and keeps
15
+ * the user's access token refreshed in the background.
16
+ *
17
+ * Relies on globals injected by the Codenotch runtime hosting page
18
+ * (`serviceName`, `tenant`, `user`, `languages`, `appManifest`…) — it is not
19
+ * usable outside a Codenotch-served application.
20
+ */
21
+ class Auml extends react_1.default.Component {
22
+ constructor(props) {
23
+ super(props);
24
+ this.state = {
25
+ loaded: false,
26
+ inspectorEnabled: false
27
+ };
28
+ if (typeof user === 'undefined') {
29
+ let globalObject = typeof window !== 'undefined' ? window : globalThis;
30
+ globalObject['user'] = null; // Make sure user is defined
31
+ }
32
+ }
33
+ componentDidMount() {
34
+ this.registerRefreshToken();
35
+ }
36
+ registerRefreshToken() {
37
+ try {
38
+ // Setup a method to refresh our access token when it expires
39
+ if (this._refreshTokenTimer) {
40
+ clearTimeout(this._refreshTokenTimer);
41
+ }
42
+ if (user === null) {
43
+ if (this.props.verbose === true) {
44
+ console.log("No user found, will not refresh the token");
45
+ }
46
+ return;
47
+ }
48
+ let timeToExpireMs = this.getTimeToTokenExpiration();
49
+ if (timeToExpireMs === null) {
50
+ if (this.props.verbose === true) {
51
+ console.log("No identity token found, attempting refreshing the token in 5 minutes");
52
+ }
53
+ this._refreshTokenTimer = setTimeout(() => this.refreshExpiredToken(), 5 * 60 * 1000);
54
+ return;
55
+ }
56
+ if (timeToExpireMs <= 0) {
57
+ // Refresh it immediatelty
58
+ this.refreshExpiredToken();
59
+ }
60
+ else {
61
+ this._refreshTokenTimer = setTimeout(() => this.refreshExpiredToken(), timeToExpireMs);
62
+ }
63
+ }
64
+ catch (err) {
65
+ console.warn("Could not setup token refresh timer: " + err);
66
+ }
67
+ }
68
+ componentWillUnmount() {
69
+ // Cleanup the refresh of the token
70
+ if (this._refreshTokenTimer) {
71
+ clearTimeout(this._refreshTokenTimer);
72
+ }
73
+ }
74
+ progress(s) {
75
+ //console.log('progessStatus ', s);
76
+ if (s === 'completed') {
77
+ this.setState({ loaded: true });
78
+ }
79
+ }
80
+ // Get input from the query parameters in the url
81
+ retrieveInputs() {
82
+ return Object.fromEntries(new URLSearchParams(window.location.search).entries());
83
+ }
84
+ getTimeToTokenExpiration() {
85
+ // The identity token is the only one we have access here on the client
86
+ // We use it to know when the access token (which we can't read) will expire
87
+ let identityToken = null;
88
+ let identityTokenKey = `${tenant.name}IdToken=`;
89
+ let cookies = document.cookie.split(';');
90
+ for (let c of cookies) {
91
+ if (c.trim().startsWith(identityTokenKey)) {
92
+ identityToken = c.trim().slice(identityTokenKey.length);
93
+ break;
94
+ }
95
+ }
96
+ if (identityToken === null) {
97
+ return null; // token not found
98
+ }
99
+ let identityTokenParsed = this.parseJwt(identityToken);
100
+ let expires = identityTokenParsed.exp; // Timestamp in second since Unix epoch
101
+ let timeToExpiresMs = expires * 1000 - new Date().getTime();
102
+ let timeToExpireStr = this.msToTime(timeToExpiresMs);
103
+ if (this.props.verbose === true) {
104
+ console.log(`Token will expire at ${new Date(expires * 1000).toISOString()} (in ${timeToExpireStr}), setting up a timer to refresh it`);
105
+ }
106
+ // Refresh it a bit before the expiration (5 min)
107
+ timeToExpiresMs -= 5 * 60 * 1000;
108
+ return timeToExpiresMs;
109
+ }
110
+ getTimeToNewTokenExpiration(expirationDateTime) {
111
+ let timeToExpiresMs = new Date(expirationDateTime).getTime() - new Date().getTime();
112
+ // Refresh it a bit before the expiration (5 min)
113
+ timeToExpiresMs -= 5 * 60 * 1000;
114
+ return timeToExpiresMs;
115
+ }
116
+ msToTime(ms) {
117
+ let seconds = Math.floor((ms / 1000) % 60), minutes = Math.floor((ms / (1000 * 60)) % 60), hours = Math.floor((ms / (1000 * 60 * 60)) % 24);
118
+ let timeString = seconds + " seconds";
119
+ if (minutes > 0) {
120
+ timeString = minutes + " minutes " + timeString;
121
+ }
122
+ if (hours > 0) {
123
+ timeString = hours + " hours " + timeString;
124
+ }
125
+ return timeString;
126
+ }
127
+ parseJwt(token) {
128
+ var base64Url = token.split('.')[1];
129
+ var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
130
+ var jsonPayload = decodeURIComponent(window.atob(base64).split('').map(function (c) {
131
+ return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
132
+ }).join(''));
133
+ return JSON.parse(jsonPayload);
134
+ }
135
+ render() {
136
+ let inputs = this.retrieveInputs();
137
+ let theme = this.props.env?.theme === "dark";
138
+ let aumlManifest = null;
139
+ try {
140
+ let appManifestObj = JSON.parse(appManifest);
141
+ aumlManifest = appManifestObj.auml;
142
+ }
143
+ catch { }
144
+ let auml = this.props.value;
145
+ if (this.props.env) {
146
+ try {
147
+ auml = I18nUtils_1.default.compileAuml(auml, this.props.env);
148
+ if (this.props.verbose === true) {
149
+ console.log("Compiled AUML with i18n variables: ", auml);
150
+ console.log("i18n variables: ", this.props.env.i18n);
151
+ }
152
+ }
153
+ catch (err) {
154
+ console.error("Error compiling AUML with i18n variables: " + err);
155
+ }
156
+ }
157
+ return react_1.default.createElement("div", { className: "app" },
158
+ react_1.default.createElement(SpaRenderWithBrowserRouter_1.SpaRenderWithBrowserRouter, { appDescription: auml, tenant: tenant, serviceName: serviceName, packageVersions: packageVersions, user: user, languages: languages, onProgress: (s) => this.progress(s), input: inputs, theme: theme, manifest: aumlManifest, inspectorEnabled: this.state.inspectorEnabled, children: [] }),
159
+ !this.state.loaded &&
160
+ react_1.default.createElement("div", { className: `app-loading ${this.props.env?.theme ?? 'light'}` },
161
+ this.renderLoadingContent(),
162
+ react_1.default.createElement("i", { className: "fas fa-circle-notch fa-spin" })));
163
+ }
164
+ renderLoadingContent() {
165
+ try {
166
+ //@ts-ignore
167
+ let tenant = global.tenant;
168
+ if (tenant.logoUrl) {
169
+ return react_1.default.createElement("img", { src: tenant.logoUrl, alt: tenant.displayName });
170
+ }
171
+ else {
172
+ return react_1.default.createElement("div", { className: 'app-loading-title' }, tenant.displayName);
173
+ }
174
+ }
175
+ catch (e) {
176
+ console.warn("Could not load tenant information: " + e);
177
+ return react_1.default.createElement("div", { className: 'app-loading-title' }, "Loading...");
178
+ }
179
+ }
180
+ async refreshExpiredToken() {
181
+ console.log("Token will expire soon, requesting a new one...");
182
+ // Using the refresh token we ask for a new access token using /portal/login/refresh
183
+ // If the refresh token has also expired, we will be redirected to the login page
184
+ let redirectUrl = window.location.href; // Where to send us back in case we need to be redirected to the login page
185
+ let url = `${tenant.clusterUrl}/portal/login/refresh?redirectUrl=${encodeURIComponent(redirectUrl)}`;
186
+ let response = await fetch(url); // For this request to work, we need to have a refresh token in the cookies
187
+ if (response.ok) {
188
+ // Setup next refresh
189
+ let timeToExpireMs;
190
+ try {
191
+ let newTokenExpiration = await response.text();
192
+ timeToExpireMs = this.getTimeToNewTokenExpiration(newTokenExpiration);
193
+ }
194
+ catch {
195
+ timeToExpireMs = 2 * 60 * 60 * 1000; // refresh in 2 hours
196
+ }
197
+ console.log(`Refresh request ok, next refresh in ${this.msToTime(timeToExpireMs)}`);
198
+ this._refreshTokenTimer = setTimeout(() => this.refreshExpiredToken(), timeToExpireMs);
199
+ }
200
+ else {
201
+ let content = await response.text();
202
+ console.error("Could not refresh the token", content);
203
+ console.log("Retrying refreshing the token in 5 minutes...");
204
+ this._refreshTokenTimer = setTimeout(() => this.refreshExpiredToken(), 5 * 60 * 1000);
205
+ }
206
+ }
207
+ }
208
+ exports.Auml = Auml;
@@ -0,0 +1,39 @@
1
+ import React from "react";
2
+ import { ICodenotchEnv } from "..";
3
+ interface ICodeEditorProps {
4
+ /** Codenotch environment; only used to follow the current theme (`vs`/`vs-dark`). */
5
+ env?: ICodenotchEnv;
6
+ style?: React.CSSProperties;
7
+ className?: string;
8
+ /** Text content of the editor. */
9
+ value?: string;
10
+ /** Monaco language id (e.g. `'typescript'`, `'json'`, `'xml'`). Defaults to `'plaintext'`. */
11
+ language?: string;
12
+ readOnly?: boolean;
13
+ /** Called (debounced, 300 ms) when the user edits the content. */
14
+ onChange?: (value: string) => void;
15
+ }
16
+ /**
17
+ * Light Monaco code editor embedded in a sandboxed iframe
18
+ * (Monaco is loaded from the jsdelivr CDN — requires network access).
19
+ *
20
+ * @example
21
+ * <CodeEditor env={cn.env} language="json" value={text} onChange={setText} />
22
+ */
23
+ export default class CodeEditor extends React.Component<ICodeEditorProps> {
24
+ private isIframeReady;
25
+ private onChangeTimeoutHandle;
26
+ private onMessageHandler;
27
+ refFrame: React.RefObject<HTMLIFrameElement>;
28
+ constructor(props: ICodeEditorProps);
29
+ setValue(value: string): void;
30
+ componentDidMount(): void;
31
+ componentWillUnmount(): void;
32
+ componentDidUpdate(prevProps: ICodeEditorProps): void;
33
+ private handleMessage;
34
+ private postToIframe;
35
+ private writeIframe;
36
+ render(): React.JSX.Element;
37
+ }
38
+ export {};
39
+ //# sourceMappingURL=CodeEditor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CodeEditor.d.ts","sourceRoot":"","sources":["../../src/components/CodeEditor.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AAEnC,UAAU,gBAAgB;IACtB,qFAAqF;IACrF,GAAG,CAAC,EAAE,aAAa,CAAC;IACpB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kCAAkC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8FAA8F;IAC9F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACtC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,KAAK,CAAC,SAAS,CAAC,gBAAgB,CAAC;IACrE,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,qBAAqB,CAAa;IAC1C,OAAO,CAAC,gBAAgB,CAAM;IAC9B,QAAQ,qCAAwC;gBAEpC,KAAK,EAAE,gBAAgB;IAKnC,QAAQ,CAAC,KAAK,EAAE,MAAM;IAStB,iBAAiB,IAAI,IAAI;IAKzB,oBAAoB,IAAI,IAAI;IAI5B,kBAAkB,CAAC,SAAS,EAAE,gBAAgB,GAAG,IAAI;IAoBrD,OAAO,CAAC,aAAa;IAgCrB,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,WAAW;IA4EnB,MAAM;CAeT"}
@@ -0,0 +1,174 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const react_1 = __importDefault(require("react"));
7
+ /**
8
+ * Light Monaco code editor embedded in a sandboxed iframe
9
+ * (Monaco is loaded from the jsdelivr CDN — requires network access).
10
+ *
11
+ * @example
12
+ * <CodeEditor env={cn.env} language="json" value={text} onChange={setText} />
13
+ */
14
+ class CodeEditor extends react_1.default.Component {
15
+ constructor(props) {
16
+ super(props);
17
+ this.isIframeReady = false;
18
+ this.onChangeTimeoutHandle = null;
19
+ this.refFrame = react_1.default.createRef();
20
+ this.onMessageHandler = this.handleMessage.bind(this);
21
+ }
22
+ setValue(value) {
23
+ if (this.isIframeReady) {
24
+ this.postToIframe({
25
+ type: 'update',
26
+ value: value,
27
+ });
28
+ }
29
+ }
30
+ componentDidMount() {
31
+ window.addEventListener('message', this.onMessageHandler);
32
+ this.writeIframe();
33
+ }
34
+ componentWillUnmount() {
35
+ window.removeEventListener('message', this.onMessageHandler);
36
+ }
37
+ componentDidUpdate(prevProps) {
38
+ const changed = (prevProps.value !== this.props.value ||
39
+ prevProps.language !== this.props.language ||
40
+ prevProps.readOnly !== this.props.readOnly);
41
+ if (changed && this.isIframeReady) {
42
+ let isDark = this.props.env?.theme === 'dark';
43
+ this.postToIframe({
44
+ type: 'update',
45
+ value: this.props.value ?? '',
46
+ language: this.props.language || 'plaintext',
47
+ readOnly: !!this.props.readOnly,
48
+ theme: isDark ? 'vs-dark' : 'vs'
49
+ });
50
+ }
51
+ }
52
+ handleMessage(event) {
53
+ const data = event.data;
54
+ if (!data || typeof data !== 'object')
55
+ return;
56
+ if (data.__from !== 'MonacoIframe')
57
+ return;
58
+ switch (data.type) {
59
+ case 'ready': {
60
+ this.isIframeReady = true;
61
+ // send initial payload
62
+ this.postToIframe({
63
+ type: 'init',
64
+ value: this.props.value ?? '',
65
+ language: this.props.language || 'plaintext',
66
+ readOnly: !!this.props.readOnly,
67
+ theme: this.props.env?.theme === 'dark' ? 'vs-dark' : 'vs'
68
+ });
69
+ break;
70
+ }
71
+ case 'change': {
72
+ const next = String(data.value ?? '');
73
+ if (this.onChangeTimeoutHandle) {
74
+ clearTimeout(this.onChangeTimeoutHandle);
75
+ }
76
+ this.onChangeTimeoutHandle = setTimeout(() => {
77
+ this.props.onChange?.(next);
78
+ }, 300);
79
+ break;
80
+ }
81
+ }
82
+ }
83
+ ;
84
+ postToIframe(message) {
85
+ const w = this.refFrame.current?.contentWindow;
86
+ if (!w)
87
+ return;
88
+ w.postMessage({ __from: 'MonacoIframe', ...message }, '*');
89
+ }
90
+ writeIframe() {
91
+ const iframe = this.refFrame.current;
92
+ if (!iframe)
93
+ return;
94
+ const vsBase = 'https://cdn.jsdelivr.net/npm/monaco-editor@0.55.1/min/vs';
95
+ const html = `<!DOCTYPE html>
96
+ <html>
97
+ <head>
98
+ <meta charset="utf-8" />
99
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
100
+ <style>
101
+ html, body, #container { height: 100%; width: 100%; margin: 0; padding: 0; overflow: hidden; }
102
+ </style>
103
+ <script>
104
+ (function(){
105
+ var vsBase = ${JSON.stringify(vsBase)};
106
+ // AMD loader
107
+ var s = document.createElement('script');
108
+ s.src = vsBase + '/loader.js';
109
+ s.onload = function(){
110
+ // Configure and load monaco
111
+ window.require.config({ paths: { vs: vsBase } });
112
+ window.require(['vs/editor/editor.main'], function(){
113
+ var editor;
114
+
115
+ function post(msg){ parent.postMessage(Object.assign({__from:'MonacoIframe'}, msg), '*'); }
116
+
117
+ function ensureEditor(){
118
+ if (editor) return editor;
119
+ editor = monaco.editor.create(document.getElementById('container'), {
120
+ value: '',
121
+ language: 'plaintext',
122
+ theme: 'vs',
123
+ automaticLayout: true,
124
+ minimap: { enabled: false },
125
+ scrollBeyondLastLine: true,
126
+ wordWrap: "on"
127
+ });
128
+ editor.onDidChangeModelContent(function(){
129
+ var v = editor.getValue();
130
+ post({ type: 'change', value: v });
131
+ });
132
+ return editor;
133
+ }
134
+
135
+ window.addEventListener('message', function(ev){
136
+ var data = ev.data || {}; if (data.__from !== 'MonacoIframe') return;
137
+ if (data.type === 'init' || data.type === 'update'){
138
+ var ed = ensureEditor();
139
+ if (typeof data.value === 'string' && ed.getValue() !== data.value){ ed.setValue(data.value); }
140
+ if (typeof data.language === 'string') { monaco.editor.setModelLanguage(ed.getModel(), data.language); }
141
+ if (typeof data.theme === 'string') { monaco.editor.setTheme(data.theme); }
142
+ if (typeof data.readOnly === 'boolean') { ed.updateOptions({ readOnly: data.readOnly }); }
143
+ }
144
+ });
145
+
146
+ // Signal ready after monaco is loaded
147
+ post({ type: 'ready' });
148
+ });
149
+ };
150
+ document.head.appendChild(s);
151
+ })();
152
+ </script>
153
+ </head>
154
+ <body>
155
+ <div id="container"></div>
156
+ </body>
157
+ </html>`;
158
+ const doc = iframe.contentWindow?.document;
159
+ if (!doc)
160
+ return;
161
+ doc.open();
162
+ doc.write(html);
163
+ doc.close();
164
+ }
165
+ render() {
166
+ let style = this.props.style || {};
167
+ style.width = style.width || '100%';
168
+ style.height = style.height || '100%';
169
+ style.border = style.border || '0';
170
+ style.outline = style.outline || '0';
171
+ return react_1.default.createElement("iframe", { style: style, title: "Codenotch", ref: this.refFrame, className: this.props.className, sandbox: "allow-scripts allow-same-origin" });
172
+ }
173
+ }
174
+ exports.default = CodeEditor;
@@ -0,0 +1,10 @@
1
+ import { IProcessCallbacks } from "../models/Misc";
2
+ import { IProcessResult } from "../models/Codenotch";
3
+ /**
4
+ * static class mainly containing a method to start processes
5
+ */
6
+ export default class ProcessUtils {
7
+ static startProcess(subscribeFunc: (processInstanceId: string, callbacks: IProcessCallbacks) => Promise<void>, clusterUrl: string, tenantName: string, projectName: string, processId: string, processInput: any, processInstanceId?: string, startNodeId?: string, token?: string): Promise<IProcessResult>;
8
+ private static launchMainProcess;
9
+ }
10
+ //# sourceMappingURL=ProcessUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProcessUtils.d.ts","sourceRoot":"","sources":["../../src/core/ProcessUtils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,YAAY;WAET,YAAY,CAAC,aAAa,EAAE,CAAC,iBAAiB,EAAE,MAAM,EAAE,SAAS,EAAE,iBAAiB,KAAK,OAAO,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,iBAAiB,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;mBAsCpS,iBAAiB;CAsCzC"}