@codenotch/codenotch.react 2.0.0 → 2.0.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/src/index.ts CHANGED
@@ -1,15 +1,11 @@
1
1
  import { createElement, useSyncExternalStore, type ComponentType, type ComponentProps, type FunctionComponent, type ReactNode, type RefAttributes } from "react";
2
2
  import { flushSync } from "react-dom";
3
3
  import { createRoot, Root } from "react-dom/client";
4
- import { SignalR } from "./core/SignalR";
5
- import ProcessUtils from "./core/ProcessUtils";
6
4
  import { IDisposable } from "./models/Misc";
7
5
  import { ICodenotchApi, ICodenotchDialog, ICodenotchEnv, IProcessResult, WithCodenotchProps } from "./models/Codenotch";
8
6
  import { v4 } from "uuid";
9
7
  import CodeEditor from "./components/CodeEditor";
10
8
 
11
- var signalR: SignalR | undefined = undefined;
12
-
13
9
  /**
14
10
  * The Codenotch runtime environment, populated by {@link init}.
15
11
  * Prefer reading it through `useCodenotch().env`.
@@ -51,7 +47,6 @@ function subscribe(listener: () => void): () => void {
51
47
  * any other source). It must be called before `useCodenotch()` is used.
52
48
  *
53
49
  * Recognized key formats:
54
- * - `i18n.<key>.<language>` — a translation, stored in `env.i18n[language][key]`.
55
50
  * The first language found becomes the current language if none is set.
56
51
  * - `GLOBAL.<name>` — JSON value assigned to `window[name]` (or `globalThis`).
57
52
  * - `appManifest` / `projectManifest` — parsed as JSON into the environment.
@@ -66,9 +61,7 @@ function subscribe(listener: () => void): () => void {
66
61
  * init({
67
62
  * clusterUrl: 'https://cluster.example.com',
68
63
  * serviceName: 'myproject',
69
- * tenantName: 'acme',
70
- * 'i18n.welcome.en': 'Welcome',
71
- * 'i18n.welcome.fr': 'Bienvenue'
64
+ * tenantName: 'acme'
72
65
  * });
73
66
  */
74
67
  function init(envVariables: any): void {
@@ -77,8 +70,6 @@ function init(envVariables: any): void {
77
70
  // Ensure envVariables is an object
78
71
  if (typeof envVariables === 'object' && envVariables !== null) {
79
72
 
80
- let i18nLanguages = new Set<string>();
81
-
82
73
  Object.keys(envVariables).forEach((key) => {
83
74
  try {
84
75
  if (key.startsWith('GLOBAL.')) {
@@ -89,23 +80,6 @@ function init(envVariables: any): void {
89
80
  console.log(`Codenotch global variable ${globalKey} set!`);
90
81
  return;
91
82
  }
92
- else if (key.startsWith("i18n.")) {
93
- // Special case
94
- let parts = key.split("."); // i18n.someKey.fr
95
- let language = parts[parts.length - 1]; // fr
96
- let keyName = parts.slice(1, parts.length - 1).join("."); // someKey
97
- i18nLanguages.add(language);
98
-
99
- let value = `${envVariables[key]}`.trim();
100
- if (value.startsWith('"') && value.endsWith('"')) {
101
- // Parse like json
102
- value = JSON.parse(value);
103
- }
104
-
105
- env.i18n = env.i18n ?? {};
106
- env.i18n[language] = env.i18n[language] ?? {};
107
- env.i18n[language][keyName] = value;
108
- }
109
83
  else {
110
84
  (env as any)[key] = envVariables[key];
111
85
 
@@ -126,20 +100,6 @@ function init(envVariables: any): void {
126
100
  console.error(`Error processing Codenotch environment variable ${key}:`, error);
127
101
  }
128
102
  });
129
-
130
- if (env.language === undefined) {
131
- // If current language is not defined, we set it to the first language found in i18n
132
- if (i18nLanguages.size > 0) {
133
- env.language = Array.from(i18nLanguages)[0];
134
- console.log(`Codenotch current language set to ${env.language}`);
135
- }
136
- }
137
-
138
- }
139
-
140
- // Instantiate SignalR for further use in the API
141
- if (env.clusterUrl && env.serviceName) {
142
- signalR = new SignalR(env.clusterUrl, env.serviceName, false, env.accessToken);
143
103
  }
144
104
 
145
105
  // Get theme like AEC does
@@ -217,57 +177,105 @@ function createApi(): ICodenotchApi {
217
177
 
218
178
  return await response.json();
219
179
  },
220
- i18n: (key: string, ...args: any[]): string => {
221
- try {
222
- if (env.i18n === undefined || Object.keys(env.i18n).length === 0) {
223
- console.warn("Codenotch i18n is not defined. Please set it in the Codenotch configuration.");
224
- return key;
225
- }
180
+ startProcess: async <TInput, TOutput>(processId: string, inputs: TInput): Promise<IProcessResult<TOutput>> => {
181
+ if (env.clusterUrl === undefined) throw new Error(MISSING_CLUSTER_URL_ERROR);
182
+ if (env.serviceName === undefined) throw new Error(MISSING_SERVICE_NAME_ERROR);
226
183
 
227
- let language = env.language ?? "en";
228
- let isLanguageExists = env.i18n[language] !== undefined;
229
- if (isLanguageExists === false) {
230
- let fallbackLanguage = Object.keys(env.i18n)[0];
231
- console.warn(`Codenotch i18n language ${language} is not defined. Falling back to ${fallbackLanguage}. Please set the correct language in the Codenotch configuration.`);
232
- language = fallbackLanguage;
184
+ // Exemple url: http://127.0.0.1:5005/test1/processes/helloWorldProcess/instances
185
+ // Format: {env.clusterUrl}/{env.serviceName}/processes/{process}/instances
186
+ /*
187
+ Response is like :
188
+ {
189
+ "instanceId": "0bb026e7-5470-486d-8164-dc83d7e32bd7",
190
+ "instanceToken": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjI5NTllZGY4LTdkY2UtND...0wYVg"
233
191
  }
192
+ */
234
193
 
235
- let dico = env.i18n[language];
236
- let value = dico[key];
237
-
238
- if (typeof value !== "string") {
239
- console.warn(`Codenotch i18n key ${key} is not defined for language ${language}. Please set it in the Codenotch configuration.`);
240
- return key;
241
- }
242
- args = args ?? [];
243
194
 
244
- let result = value.replace(/{([\d ]*)}/g, (match, p1) => {
245
- let argValue = args[p1.trim()];
246
- // Instead of returning 'undefined', returns an empty string
247
- return argValue !== undefined ? argValue : "";
195
+ // Starts the process
196
+ const response = await fetch(`${env.clusterUrl}/${env.serviceName}/processes/${processId}/instances`,
197
+ {
198
+ method: 'POST',
199
+ body: JSON.stringify({
200
+ "id": v4(),
201
+ "input": inputs
202
+ })
248
203
  });
204
+ if (response.status.toString().startsWith('2') === false) {
205
+ console.warn("Codenotch startProcess: failed to start process", response.status, response.statusText);
206
+ return {
207
+ isError: true,
208
+ output: undefined as any,
209
+ processInstanceId: '',
210
+ errorMessage: `Failed to start process: ${response.status} ${response.statusText}`
211
+ };
212
+ }
249
213
 
250
- return result;
214
+ // Check response
215
+ const instanceData: { instanceId: string; instanceToken: string } = await response.json();
216
+ if (!instanceData.instanceId || !instanceData.instanceToken) {
217
+ console.warn("Codenotch startProcess: invalid response data", instanceData);
218
+ return {
219
+ isError: true,
220
+ output: undefined as any,
221
+ processInstanceId: '',
222
+ errorMessage: `Failed to start process: invalid response data`
223
+ };
251
224
  }
252
- catch (error) {
253
- console.error("Error in i18n function:", error);
254
- return key;
225
+
226
+ const getProcessResult = async () => {
227
+ // Instance get: http://127.0.0.1:5005/instances/826bd498-e410-4c48-b29a-e79a76b75ef1
228
+ // Token header: Codenotch-Instance-Token
229
+ /*
230
+ {
231
+ "id": "826bd498-e410-4c48-b29a-e79a76b75ef1",
232
+ "state": "SUCCESS",
233
+ "output": {
234
+ "result": "Hello, World!"
235
+ },
236
+ "errors": []
237
+ }
238
+ */
239
+ const instanceResp = await fetch(`${env.clusterUrl}/instances/${instanceData.instanceId}`, {
240
+ method: 'GET',
241
+ headers: {
242
+ 'Codenotch-Instance-Token': instanceData.instanceToken
243
+ }
244
+ });
245
+ const instanceDataResp: { state: string; output: any; errors: any[] } = await instanceResp.json();
246
+ if (instanceDataResp.state === 'SUCCESS') {
247
+ return instanceDataResp.output;
248
+ }
249
+ console.log("instanceDataResp", instanceDataResp);
250
+ return undefined;
251
+ };
252
+
253
+
254
+ const timeout = 30000; // 30 seconds
255
+ const pollInterval = 500; // 0.5 second
256
+ const startTime = Date.now();
257
+ while (Date.now() - startTime < timeout) {
258
+ const result = await getProcessResult();
259
+ if (result !== undefined) {
260
+ return {
261
+ isError: false,
262
+ output: result as TOutput,
263
+ processInstanceId: instanceData.instanceId,
264
+ errorMessage: ''
265
+ };
266
+ }
267
+ await new Promise(resolve => setTimeout(resolve, pollInterval));
255
268
  }
256
- },
257
- startProcess: async (processName: any, startNodeId: any, inputs: any): Promise<IProcessResult> => {
258
- if (env.clusterUrl === undefined) throw new Error(MISSING_CLUSTER_URL_ERROR);
259
- if (env.serviceName === undefined) throw new Error(MISSING_SERVICE_NAME_ERROR);
260
- if (env.tenantName === undefined) throw new Error(MISSING_TENANT_NAME_ERROR);
261
- if (signalR === undefined) throw new Error("SignalR not available. Cannot start process.");
262
- return ProcessUtils.startProcess((processInstanceId, callbacks) => signalR!.subscribeToProcessInstance(processInstanceId, callbacks),
263
- env.clusterUrl,
264
- env.tenantName,
265
- env.serviceName,
266
- processName,
267
- inputs,
268
- undefined,
269
- startNodeId ?? 'start',
270
- env.accessToken);
269
+
270
+ console.warn(`Codenotch startProcess: process ${processId} timed out after ${timeout} ms`);
271
+ return {
272
+ isError: true,
273
+ output: undefined as any,
274
+ processInstanceId: instanceData.instanceId,
275
+ errorMessage: `Process timed out after ${timeout} ms`
276
+ };
277
+
278
+
271
279
  },
272
280
  getUrlParams: () => {
273
281
  let result: { [key: string]: string } = {};
@@ -345,21 +353,6 @@ function createApi(): ICodenotchApi {
345
353
 
346
354
  return result;
347
355
  },
348
- listenSignal: async (signalId: string, callback: (data: any) => void): Promise<IDisposable> => {
349
-
350
- if (!signalR) {
351
- throw new Error("SignalR not available. Cannot listen to signal.");
352
- }
353
-
354
- let unsubscribeFunc = await signalR.subscribeToSignal(signalId, callback);
355
-
356
- return {
357
- dispose: () => {
358
- unsubscribeFunc();
359
- }
360
- };
361
-
362
- },
363
356
  setTheme: (theme: 'light' | 'dark') => {
364
357
  if (env.theme === theme) return;
365
358
  if (env.theme) document.documentElement.classList.remove(env.theme);
@@ -427,7 +420,7 @@ function getCodenotch(): ICodenotchApi {
427
420
  *
428
421
  * The reference is stable across renders and only changes when the environment
429
422
  * changes (`setLanguage`, `setTheme`, `init`), in which case every component
430
- * using the hook re-renders — so `cn.i18n(...)` output follows the current
423
+ * using the hook re-renders output follows the current
431
424
  * language automatically, and the object is safe to use in `useMemo` /
432
425
  * `useEffect` dependency arrays.
433
426
  *
@@ -435,15 +428,6 @@ function getCodenotch(): ICodenotchApi {
435
428
  * component or a custom hook). Outside of components — handlers defined in
436
429
  * plain modules, services, class components — use {@link getCodenotch} or
437
430
  * {@link withCodenotch} instead.
438
- *
439
- * @returns The Codenotch API: BPMN processes, CNQL queries, i18n, signals, theme…
440
- * @example
441
- * import { useCodenotch } from 'codenotch-react';
442
- *
443
- * const MyApp = () => {
444
- * const cn = useCodenotch();
445
- * return <h1>{cn.i18n('welcome')}</h1>;
446
- * };
447
431
  */
448
432
  function useCodenotch(): ICodenotchApi {
449
433
  return useSyncExternalStore(subscribe, getCodenotch, getCodenotch);
@@ -485,21 +469,6 @@ type ComponentInstance<C> = C extends new (...args: any[]) => infer I ? I : neve
485
469
  *
486
470
  * @param Component A component whose props extend {@link WithCodenotchProps}.
487
471
  * @returns A component with the same props minus `cn`.
488
- * @example
489
- * import { withCodenotch, WithCodenotchProps } from 'codenotch-react';
490
- *
491
- * interface Props extends WithCodenotchProps {
492
- * userId: string;
493
- * }
494
- *
495
- * class TodoList extends React.Component<Props> {
496
- * render() {
497
- * return <h1>{this.props.cn.i18n('todos.title')}</h1>;
498
- * }
499
- * }
500
- *
501
- * export default withCodenotch(TodoList);
502
- * // <TodoList userId="42" /> — `cn` is injected
503
472
  */
504
473
  function withCodenotch<C extends ComponentType<any>>(
505
474
  Component: C
@@ -523,10 +492,6 @@ export {
523
492
  CodeEditor
524
493
  };
525
494
 
526
- // Public models. Re-exported from the package root so that the typings
527
- // generated by the Codenotch IDE (typings/i18n.d.ts and typings/process.d.ts,
528
- // which augment the 'codenotch-react' module) merge with the real
529
- // TranslationRegistry / ProcessRegistry declarations.
530
495
  export * from "./models/Codenotch";
531
496
  export * from "./models/Misc";
532
497
  export * from "@codenotch/codenotch.core";
@@ -1,5 +1,5 @@
1
1
  import type { ReactNode } from "react";
2
- import { ProjectManifest, IAppManifest } from "@codenotch/codenotch.core";
2
+ import { IAppManifest, ICodenotchProjectManifest } from "@codenotch/codenotch.core";
3
3
  import { ICodenotchSignal, IDisposable } from "./Misc";
4
4
 
5
5
  /**
@@ -21,9 +21,7 @@ export interface ICodenotchEnv {
21
21
  accessToken?: string;
22
22
  /** Base URL of the current application. */
23
23
  baseUrl?: string;
24
- /** Translation dictionaries, keyed by language then by translation key: `i18n[language][key] = text`. */
25
- i18n?: { [language: string]: { [key: string]: string } };
26
- /** Current language code (e.g. `"en"`). Defaults to the first language found in {@link i18n}. */
24
+ /** Current language code (e.g. `"en"`). */
27
25
  language?: string;
28
26
  /** Current UI theme. Resolved from the `_theme` URL parameter, or `prefers-color-scheme` as a fallback. */
29
27
  theme?: 'light' | 'dark';
@@ -31,94 +29,12 @@ export interface ICodenotchEnv {
31
29
  /** Manifest of the current application (the `<AppName>.manifest.json` file next to the app). */
32
30
  appManifest?: IAppManifest;
33
31
  /** Manifest of the Codenotch project (the project's `manifest.json` file). */
34
- projectManifest?: ProjectManifest;
32
+ projectManifest?: ICodenotchProjectManifest;
35
33
 
36
34
  /** Any additional environment variable passed to `init()`. */
37
35
  [key: string]: any; // Allow additional properties
38
36
  }
39
37
 
40
-
41
- /**
42
- * Registry of the project's BPMN processes, used to type {@link ICodenotchApi.startProcess}.
43
- *
44
- * Empty by default: the Codenotch IDE fills it through declaration merging in the
45
- * auto-generated `typings/process.d.ts` file of each project, with one entry per
46
- * BPMN process. Each entry maps the process name to its start events (`nodes`,
47
- * keyed by start node id, valued with the start event's input parameters) and to
48
- * the `output` of its end event.
49
- *
50
- * When the registry has not been augmented (project built without the IDE
51
- * typings), `startProcess` falls back to plain string names and untyped inputs,
52
- * so the code still compiles.
53
- *
54
- * @example
55
- * // typings/process.d.ts (auto-generated by the Codenotch IDE)
56
- * import 'codenotch-react';
57
- * declare module 'codenotch-react' {
58
- * interface ProcessRegistry {
59
- * 'getTodos': {
60
- * nodes: { 'start': { UserId: string } };
61
- * output: { todos: any[] };
62
- * };
63
- * }
64
- * }
65
- */
66
- export interface ProcessRegistry {
67
- // Extended by codenotch IDE
68
- }
69
-
70
- /**
71
- * Registry of the project's translation keys, used to type {@link ICodenotchApi.i18n}.
72
- *
73
- * Empty by default: the Codenotch IDE fills it through declaration merging in the
74
- * auto-generated `typings/i18n.d.ts` file of each project, from the project's
75
- * `.i18n.csv` files. Each entry maps a translation key to the tuple of arguments
76
- * expected by its `{0}`, `{1}`, … placeholders.
77
- *
78
- * When the registry has not been augmented (project built without the IDE
79
- * typings), `i18n` falls back to plain string keys and untyped arguments,
80
- * so the code still compiles.
81
- *
82
- * @example
83
- * // typings/i18n.d.ts (auto-generated by the Codenotch IDE)
84
- * import 'codenotch-react';
85
- * declare module 'codenotch-react' {
86
- * interface TranslationRegistry {
87
- * 'welcome': [];
88
- * 'greeting': [arg1: any, arg2: any];
89
- * }
90
- * }
91
- */
92
- export interface TranslationRegistry {
93
- // Extended by codenotch IDE
94
- }
95
-
96
- // When the codenotch IDE has not augmented the registries, `keyof Registry` is `never`
97
- // and every call would fail to compile. These helpers fall back to plain string keys
98
- // and untyped arguments so the API stays usable without code generation.
99
-
100
- /** A BPMN process name: a key of {@link ProcessRegistry}, or any string when the registry is empty. */
101
- export type ProcessName = keyof ProcessRegistry extends never ? string : keyof ProcessRegistry;
102
-
103
- /** The start nodes of process `P`: its `nodes` map from {@link ProcessRegistry}, or an open map when unknown. */
104
- export type ProcessNodes<P> = P extends keyof ProcessRegistry
105
- ? ProcessRegistry[P] extends { nodes: infer N } ? N : { [nodeId: string]: any }
106
- : { [nodeId: string]: any };
107
-
108
- /** The end-event output of process `P` from {@link ProcessRegistry}, or `any` when unknown. */
109
- export type ProcessOutput<P> = P extends keyof ProcessRegistry
110
- ? ProcessRegistry[P] extends { output: infer O } ? O : any
111
- : any;
112
-
113
- /** A translation key: a key of {@link TranslationRegistry}, or any string when the registry is empty. */
114
- export type TranslationKey = keyof TranslationRegistry extends never ? string : keyof TranslationRegistry;
115
-
116
- /** The placeholder arguments of translation key `K` from {@link TranslationRegistry}, or `any[]` when unknown. */
117
- export type TranslationArgs<K> = K extends keyof TranslationRegistry
118
- ? TranslationRegistry[K] extends any[] ? TranslationRegistry[K] : any[]
119
- : any[];
120
-
121
-
122
38
  /**
123
39
  * Result of a BPMN process execution started with {@link ICodenotchApi.startProcess}.
124
40
  */
@@ -142,56 +58,17 @@ export interface IProcessResult<T = any> {
142
58
  * plain `getCodenotch()` function anywhere else (handlers, modules, services).
143
59
  *
144
60
  * It is the bridge between a React app and the Codenotch runtime: it starts
145
- * server-side BPMN processes, runs CNQL queries, translates i18n keys,
61
+ * server-side BPMN processes, runs CNQL queries,
146
62
  * listens to real-time signals and manages theme/language.
147
- *
148
- * @example
149
- * import { useCodenotch } from 'codenotch-react';
150
- *
151
- * const cn = useCodenotch();
152
- * const title = cn.i18n('welcome');
153
- * const result = await cn.startProcess('getTodos', 'start', { UserId: '42' });
154
63
  */
155
64
  export interface ICodenotchApi {
156
65
 
157
66
  /**
158
67
  * Start a server-side BPMN process and wait for its completion.
159
- *
160
- * @param processName Name of the process — the `.bpmn` file name without extension (e.g. `processes/getTodos.bpmn` → `'getTodos'`).
161
- * @param startNodeId Id of the start event to trigger (conventionally `'start'`).
162
- * @param inputs Input parameters declared by that start event.
163
- * @returns The process result; check `isError` before using `output` (the parameters of the end event reached).
164
- * @example
165
- * const result = await cn.startProcess('getTodos', 'start', { UserId: userId });
166
- * if (!result.isError) {
167
- * setTodos(result.output.todos);
168
- * }
169
- */
170
- startProcess<P extends ProcessName, N extends keyof ProcessNodes<P>>(
171
- processName: P,
172
- startNodeId: N,
173
- inputs: ProcessNodes<P>[N]
174
- ): Promise<IProcessResult<ProcessOutput<P>>>;
175
-
176
- /**
177
- * Translate a key for the current language ({@link ICodenotchEnv.language}).
178
- *
179
- * Placeholders `{0}`, `{1}`, … in the translated text are replaced by `args`.
180
- * Never throws: if the key or the language is missing, a warning is logged
181
- * and the key itself is returned.
182
- *
183
- * @param key Translation key, as defined in the project's `.i18n.csv` files.
184
- * @param args Values for the `{0}`, `{1}`, … placeholders of the translation.
185
- * @example
186
- * cn.i18n('welcome'); // "Bienvenue"
187
- * cn.i18n('greeting', 'Fabian', 3); // "Bonjour Fabian, 3 messages" (from "Bonjour {0}, {1} messages")
188
68
  */
189
- i18n<K extends TranslationKey>(
190
- key: K,
191
- ...args: TranslationArgs<K>
192
- ): string;
69
+ startProcess<TInput, TOutput>(processId: string, inputs: TInput): Promise<IProcessResult<TOutput>>;
193
70
 
194
- /** Runtime environment (cluster, service, language, theme, i18n dictionaries, manifests…). Populated by `init()`. */
71
+ /** Runtime environment (cluster, service, language, theme dictionaries, manifests…). Populated by `init()`. */
195
72
  readonly env: ICodenotchEnv;
196
73
 
197
74
  /**
@@ -243,26 +120,11 @@ export interface ICodenotchApi {
243
120
  */
244
121
  readonly getProjectFileUrl: (relativePath: string) => string;
245
122
 
246
- /**
247
- * Subscribe to a real-time signal emitted by the project's BPMN processes (via SignalR).
248
- *
249
- * @param signalId Id of the signal, as declared in the BPMN process.
250
- * @param callback Called each time the signal is received, with `{ signalId, data }`.
251
- * @returns A disposable — call `dispose()` to unsubscribe (e.g. in a `useEffect` cleanup).
252
- * @example
253
- * useEffect(() => {
254
- * let sub: IDisposable | undefined;
255
- * cn.listenSignal('todosChanged', () => refresh()).then(s => sub = s);
256
- * return () => sub?.dispose();
257
- * }, []);
258
- */
259
- readonly listenSignal: (signalId: string, callback: (data: ICodenotchSignal) => void) => Promise<IDisposable>;
260
-
261
123
  /** Set the UI theme. Also toggles the `light`/`dark` class on `<html>` (Tailwind `dark:` variants). */
262
124
  readonly setTheme: (theme: 'light' | 'dark') => void;
263
125
  /** Current UI theme, if resolved. */
264
126
  readonly getTheme: () => 'light' | 'dark' | undefined;
265
- /** Set the current language used by {@link i18n}. Must be one of the project's languages. */
127
+ /** Set the current language. Must be one of the project's languages. */
266
128
  readonly setLanguage: (lang: string) => void;
267
129
  /** Current language code, if any. */
268
130
  readonly getLanguage: () => string | undefined;
@@ -273,7 +135,7 @@ export interface ICodenotchApi {
273
135
  /** Generate a random UUID v4. */
274
136
  readonly uuid: () => string;
275
137
  /** Manifest of the Codenotch project. Throws if not available in the environment. */
276
- readonly getProjectManifest: () => ProjectManifest;
138
+ readonly getProjectManifest: () => ICodenotchProjectManifest;
277
139
  /** Manifest of the current application, if any. */
278
140
  readonly getAppManifest: () => IAppManifest | undefined;
279
141
  }