libmodulor 0.13.0 → 0.14.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.
- package/CHANGELOG.md +10 -0
- package/README.md +2 -2
- package/dist/esm/apps/Helper/src/lib/project.js +5 -5
- package/dist/esm/index.react.d.ts +1 -0
- package/dist/esm/index.react.js +1 -0
- package/dist/esm/target/lib/react/UCPanel.js +23 -51
- package/dist/esm/target/lib/react/panel.d.ts +3 -2
- package/dist/esm/target/lib/react/useAction.d.ts +24 -0
- package/dist/esm/target/lib/react/useAction.js +57 -0
- package/dist/esm/testing/impl/TypeScriptLibUCDefASTParser.js +1 -0
- package/dist/esm/uc/exec.d.ts +15 -0
- package/dist/esm/uc/exec.js +9 -0
- package/package.json +10 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
## v0.14.0 (2025-05-24)
|
|
4
|
+
|
|
5
|
+
- Introduce `useAction` for react targets : it's a use case agnostic way of invoking an action
|
|
6
|
+
|
|
7
|
+
## v0.13.1 (2025-05-02)
|
|
8
|
+
|
|
9
|
+
**Fixed**
|
|
10
|
+
|
|
11
|
+
- Disable TypeScript [incremental](https://www.typescriptlang.org/tsconfig/#incremental) when analyzing app sources in automated test
|
|
12
|
+
|
|
3
13
|
## v0.13.0 (2025-04-25)
|
|
4
14
|
|
|
5
15
|
**Added**
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/libmodulor)
|
|
4
4
|
[](https://github.com/c100k/libmodulor/blob/master/LICENSE)
|
|
5
5
|
|
|
6
|
-
A TypeScript library to create
|
|
6
|
+
A TypeScript library to create platform-agnostic applications.
|
|
7
7
|
|
|
8
8
|
## 🚀 Getting Started
|
|
9
9
|
|
|
@@ -17,4 +17,4 @@ If you think you can help in any way, feel free to contact me (cf. `author` in `
|
|
|
17
17
|
|
|
18
18
|
## ⚖️ License
|
|
19
19
|
|
|
20
|
-
[LGPL-3.0](https://github.com/c100k/libmodulor/blob/v0.
|
|
20
|
+
[LGPL-3.0](https://github.com/c100k/libmodulor/blob/v0.14.0/LICENSE)
|
|
@@ -42,18 +42,18 @@ export const PACKAGE_JSON = (name) => `{
|
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@biomejs/biome": "^1.9.4",
|
|
45
|
-
"@types/node": "^22.
|
|
46
|
-
"@vitest/coverage-v8": "^3.1.
|
|
45
|
+
"@types/node": "^22.15.19",
|
|
46
|
+
"@vitest/coverage-v8": "^3.1.3",
|
|
47
47
|
"buffer": "^6.0.3",
|
|
48
48
|
"cookie-parser": "^1.4.7",
|
|
49
49
|
"express": "^5.1.0",
|
|
50
50
|
"express-fileupload": "^1.5.1",
|
|
51
51
|
"fast-check": "^4.1.1",
|
|
52
52
|
"helmet": "^8.1.0",
|
|
53
|
-
"jose": "^6.0.
|
|
53
|
+
"jose": "^6.0.11",
|
|
54
54
|
"typescript": "^5.8.3",
|
|
55
|
-
"vite": "^6.3.
|
|
56
|
-
"vitest": "^3.1.
|
|
55
|
+
"vite": "^6.3.5",
|
|
56
|
+
"vitest": "^3.1.3"
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
`;
|
|
@@ -6,5 +6,6 @@ export { UCContainer } from './target/lib/react/UCContainer.js';
|
|
|
6
6
|
export { UCEntrypoint } from './target/lib/react/UCEntrypoint.js';
|
|
7
7
|
export { type Props as UCOutputFieldValueFragmentProps, UCOutputFieldValueFragment, } from './target/lib/react/UCOutputFieldValueFragment.js';
|
|
8
8
|
export { UCPanel } from './target/lib/react/UCPanel.js';
|
|
9
|
+
export { type UseActionOpts, useAction, } from './target/lib/react/useAction.js';
|
|
9
10
|
export { type CloneFunc, type DivertFunc, type RefillFunc, useUC, } from './target/lib/react/useUC.js';
|
|
10
11
|
export { type AppendFunc, type RemoveFunc, type UpdateFunc, useUCOR, } from './target/lib/react/useUCOR.js';
|
package/dist/esm/index.react.js
CHANGED
|
@@ -3,5 +3,6 @@ export { UCContainer } from './target/lib/react/UCContainer.js';
|
|
|
3
3
|
export { UCEntrypoint } from './target/lib/react/UCEntrypoint.js';
|
|
4
4
|
export { UCOutputFieldValueFragment, } from './target/lib/react/UCOutputFieldValueFragment.js';
|
|
5
5
|
export { UCPanel } from './target/lib/react/UCPanel.js';
|
|
6
|
+
export { useAction, } from './target/lib/react/useAction.js';
|
|
6
7
|
export { useUC, } from './target/lib/react/useUC.js';
|
|
7
8
|
export { useUCOR, } from './target/lib/react/useUCOR.js';
|
|
@@ -1,27 +1,31 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
2
|
import { ucIsDisabled, ucIsLoading, } from '../../../uc/index.js';
|
|
3
3
|
import { sleep } from '../../../utils/index.js';
|
|
4
4
|
import { useDIContext } from './DIContextProvider.js';
|
|
5
5
|
import { UCContainer } from './UCContainer.js';
|
|
6
|
+
import { useAction } from './useAction.js';
|
|
6
7
|
export function UCPanel({ autoExec = false, clearAfterExec = true, onDone, onError, onInit, onStartSubmitting, renderAutoExecLoader, renderForm, renderExecTouchable, sleepInMs, uc, }) {
|
|
7
8
|
const { container } = useDIContext();
|
|
8
9
|
const [ucManager] = useState(container.get('UCManager'));
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
10
|
+
const { exec, execState } = useAction({
|
|
11
|
+
action: async () => {
|
|
12
|
+
const ucor = await ucManager.execClient(uc);
|
|
13
|
+
await onDone?.(ucor);
|
|
14
|
+
clear();
|
|
15
|
+
},
|
|
16
|
+
autoExec,
|
|
17
|
+
confirm: async () => await ucManager.confirmClient(uc),
|
|
18
|
+
onError,
|
|
19
|
+
onInit: async () => await onInit?.(uc),
|
|
20
|
+
onStart: async () => {
|
|
21
|
+
// Is some targets, the confirmClient blocks the main thread (e.g. window.confirm()).
|
|
22
|
+
// This leads to the state set above not being updated.
|
|
23
|
+
// This is a "hacky" workaroud to let React re-render the control with 'submitting' state before
|
|
24
|
+
await sleep(100);
|
|
25
|
+
await onStartSubmitting?.();
|
|
26
|
+
},
|
|
27
|
+
sleepInMs,
|
|
28
|
+
});
|
|
25
29
|
const onChange = (f, op, v) => {
|
|
26
30
|
f.setValue(op, v);
|
|
27
31
|
};
|
|
@@ -31,38 +35,6 @@ export function UCPanel({ autoExec = false, clearAfterExec = true, onDone, onErr
|
|
|
31
35
|
}
|
|
32
36
|
uc.clear();
|
|
33
37
|
};
|
|
34
|
-
const onSubmit = async () => {
|
|
35
|
-
setExecState('submitting');
|
|
36
|
-
// Is some targets, the confirmClient blocks the main thread (e.g. window.confirm()).
|
|
37
|
-
// This leads to the state set above not being updated.
|
|
38
|
-
// This is a "hacky" workaroud to let React re-render the control with 'submitting' state before
|
|
39
|
-
await sleep(100);
|
|
40
|
-
await onStartSubmitting?.();
|
|
41
|
-
const confirmed = await ucManager.confirmClient(uc);
|
|
42
|
-
if (!confirmed) {
|
|
43
|
-
setExecState('idle');
|
|
44
|
-
return false;
|
|
45
|
-
}
|
|
46
|
-
if (sleepInMs !== undefined) {
|
|
47
|
-
await sleep(sleepInMs);
|
|
48
|
-
}
|
|
49
|
-
try {
|
|
50
|
-
const ucor = await ucManager.execClient(uc);
|
|
51
|
-
await onDone?.(ucor);
|
|
52
|
-
clear();
|
|
53
|
-
return true;
|
|
54
|
-
}
|
|
55
|
-
catch (err) {
|
|
56
|
-
if (!onError) {
|
|
57
|
-
throw err;
|
|
58
|
-
}
|
|
59
|
-
onError(err);
|
|
60
|
-
return false;
|
|
61
|
-
}
|
|
62
|
-
finally {
|
|
63
|
-
setExecState('idle');
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
38
|
const disabled = ucIsDisabled(execState);
|
|
67
39
|
const loading = ucIsLoading(execState);
|
|
68
40
|
// TODO : Keep these as a state to avoid recomputation
|
|
@@ -79,12 +51,12 @@ export function UCPanel({ autoExec = false, clearAfterExec = true, onDone, onErr
|
|
|
79
51
|
!needsInputFilling &&
|
|
80
52
|
renderExecTouchable({
|
|
81
53
|
...ctx,
|
|
82
|
-
onSubmit,
|
|
54
|
+
onSubmit: exec,
|
|
83
55
|
}),
|
|
84
56
|
needsInputFilling &&
|
|
85
57
|
renderForm({
|
|
86
58
|
...ctx,
|
|
87
59
|
onChange,
|
|
88
|
-
onSubmit,
|
|
60
|
+
onSubmit: exec,
|
|
89
61
|
})))));
|
|
90
62
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { UC, UCExecState, UCInput, UCOPIBase, UCOutputReader } from '../../../uc/index.js';
|
|
2
|
+
import type { UseActionExec, UseActionOnError } from './useAction.js';
|
|
2
3
|
export type UCPanelOnDone<I extends UCInput | undefined = undefined, OPI0 extends UCOPIBase | undefined = undefined, OPI1 extends UCOPIBase | undefined = undefined> = (ucor: UCOutputReader<I, OPI0, OPI1>) => Promise<void>;
|
|
3
|
-
export type UCPanelOnError =
|
|
4
|
+
export type UCPanelOnError = UseActionOnError;
|
|
4
5
|
export type UCPanelOnInit<I extends UCInput | undefined = undefined, OPI0 extends UCOPIBase | undefined = undefined, OPI1 extends UCOPIBase | undefined = undefined> = (uc: UC<I, OPI0, OPI1>) => Promise<void>;
|
|
5
6
|
export type UCPanelOnStartSubmitting = () => Promise<void>;
|
|
6
|
-
export type UCPanelOnSubmit =
|
|
7
|
+
export type UCPanelOnSubmit = UseActionExec;
|
|
7
8
|
export interface UCPanelState {
|
|
8
9
|
clearAfterExec?: boolean;
|
|
9
10
|
disabled: boolean;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ErrorMessage, UIntDuration } from '../../../dt/index.js';
|
|
2
|
+
import type { UCExecRes, UCExecState } from '../../../uc/exec.js';
|
|
3
|
+
export type UseActionAction = () => Promise<void>;
|
|
4
|
+
export type UseActionConfirm = () => Promise<boolean>;
|
|
5
|
+
export type UseActionExec = () => Promise<UCExecRes>;
|
|
6
|
+
export type UseActionOnError = (err: Error) => Promise<void>;
|
|
7
|
+
export type UseActionOnInit = () => Promise<void>;
|
|
8
|
+
export type UseActionOnStart = () => Promise<void>;
|
|
9
|
+
export interface UseActionOpts {
|
|
10
|
+
action: UseActionAction;
|
|
11
|
+
autoExec?: boolean;
|
|
12
|
+
confirm?: UseActionConfirm;
|
|
13
|
+
onError?: UseActionOnError | undefined;
|
|
14
|
+
onInit?: UseActionOnInit;
|
|
15
|
+
onStart?: UseActionOnStart;
|
|
16
|
+
sleepInMs?: UIntDuration | undefined;
|
|
17
|
+
}
|
|
18
|
+
export interface UseActionRes {
|
|
19
|
+
errMsg: ErrorMessage | null;
|
|
20
|
+
exec: UseActionExec;
|
|
21
|
+
execRes: UCExecRes | null;
|
|
22
|
+
execState: UCExecState;
|
|
23
|
+
}
|
|
24
|
+
export declare function useAction({ action, autoExec, confirm, onError, onInit, onStart, sleepInMs, }: UseActionOpts): UseActionRes;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { sleep } from '../../../utils/index.js';
|
|
3
|
+
export function useAction({ action, autoExec = false, confirm, onError, onInit, onStart, sleepInMs, }) {
|
|
4
|
+
const [errMsg, setErrMsg] = useState(null);
|
|
5
|
+
const [execRes, setExecRes] = useState(null);
|
|
6
|
+
const [execState, setExecState] = useState(onInit ? 'initializing' : 'idle');
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
if (execState !== 'initializing') {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
(async () => {
|
|
12
|
+
await onInit?.();
|
|
13
|
+
setExecState('idle');
|
|
14
|
+
})();
|
|
15
|
+
}, [execState, onInit]);
|
|
16
|
+
const exec = async () => {
|
|
17
|
+
setErrMsg(null);
|
|
18
|
+
setExecRes(null);
|
|
19
|
+
setExecState('submitting');
|
|
20
|
+
await onStart?.();
|
|
21
|
+
const confirmed = confirm ? await confirm?.() : true;
|
|
22
|
+
if (!confirmed) {
|
|
23
|
+
setExecState('idle');
|
|
24
|
+
setExecRes('aborted');
|
|
25
|
+
return 'aborted';
|
|
26
|
+
}
|
|
27
|
+
if (sleepInMs !== undefined) {
|
|
28
|
+
await sleep(sleepInMs);
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
await action();
|
|
32
|
+
setExecRes('succeeded');
|
|
33
|
+
return 'succeeded';
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
setExecRes('failed');
|
|
37
|
+
if (onError) {
|
|
38
|
+
await onError?.(err);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
setErrMsg(err.message);
|
|
42
|
+
}
|
|
43
|
+
return 'failed';
|
|
44
|
+
}
|
|
45
|
+
finally {
|
|
46
|
+
setExecState('idle');
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies(exec): it complains if I add it AND if I don't add it !
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
if (!autoExec) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
exec();
|
|
55
|
+
}, [autoExec]);
|
|
56
|
+
return { errMsg, exec, execRes, execState };
|
|
57
|
+
}
|
|
@@ -92,6 +92,7 @@ let TypeScriptLibUCDefASTParser = class TypeScriptLibUCDefASTParser {
|
|
|
92
92
|
target: ScriptTarget[(compilerOptionsBase.target ??
|
|
93
93
|
target)],
|
|
94
94
|
};
|
|
95
|
+
this.compilerOptions.incremental = false; // Otherwise it triggers the following error : Option '--incremental' can only be specified using tsconfig, emitting to single file or when option '--tsBuildInfoFile' is specified.
|
|
95
96
|
// @ts-ignore
|
|
96
97
|
this.compilerOptions.jsx = undefined; // Otherwise it triggers the following error since TS 5.5 : jsx is a string value; tsconfig JSON must be parsed with parseJsonSourceFileConfigFileContent or getParsedCommandLineOfConfigFile before passing to createProgram
|
|
97
98
|
// @ts-ignore
|
package/dist/esm/uc/exec.d.ts
CHANGED
|
@@ -14,6 +14,14 @@ export declare enum UCExecMode {
|
|
|
14
14
|
*/
|
|
15
15
|
USER = "user"
|
|
16
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Result of execution of a use case
|
|
19
|
+
*
|
|
20
|
+
* - `aborted` : The user aborted the exec (e.g. by not confirming)
|
|
21
|
+
* - `failed` : The exec failed
|
|
22
|
+
* - `succeeded` : The exec succeeded
|
|
23
|
+
*/
|
|
24
|
+
export type UCExecRes = 'aborted' | 'failed' | 'succeeded';
|
|
17
25
|
/**
|
|
18
26
|
* State of execution of a use case
|
|
19
27
|
*
|
|
@@ -44,3 +52,10 @@ export declare function ucIsDisabled(execState: UCExecState): boolean;
|
|
|
44
52
|
* @returns
|
|
45
53
|
*/
|
|
46
54
|
export declare function ucIsLoading(execState: UCExecState): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Check whether the execution corresponds to an "error" result
|
|
57
|
+
*
|
|
58
|
+
* @param execRes
|
|
59
|
+
* @returns
|
|
60
|
+
*/
|
|
61
|
+
export declare function ucIsOnErr(execRes: UCExecRes | null): boolean;
|
package/dist/esm/uc/exec.js
CHANGED
|
@@ -38,3 +38,12 @@ export function ucIsDisabled(execState) {
|
|
|
38
38
|
export function ucIsLoading(execState) {
|
|
39
39
|
return execState === 'submitting';
|
|
40
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Check whether the execution corresponds to an "error" result
|
|
43
|
+
*
|
|
44
|
+
* @param execRes
|
|
45
|
+
* @returns
|
|
46
|
+
*/
|
|
47
|
+
export function ucIsOnErr(execRes) {
|
|
48
|
+
return execRes === 'failed';
|
|
49
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "libmodulor",
|
|
3
|
-
"description": "A TypeScript library to create
|
|
4
|
-
"version": "0.
|
|
3
|
+
"description": "A TypeScript library to create platform-agnostic applications",
|
|
4
|
+
"version": "0.14.0",
|
|
5
5
|
"license": "LGPL-3.0",
|
|
6
6
|
"author": "Chafik H'nini <chafik.hnini@gmail.com>",
|
|
7
7
|
"homepage": "https://libmodulor.c100k.eu",
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
},
|
|
83
83
|
"peerDependencies": {
|
|
84
84
|
"@hono/node-server": "^1.14.1",
|
|
85
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
85
|
+
"@modelcontextprotocol/sdk": "^1.11.4",
|
|
86
86
|
"@stricli/core": "^1.1.2",
|
|
87
87
|
"buffer": "^6.0.3",
|
|
88
88
|
"cookie-parser": "^1.4.7",
|
|
@@ -90,20 +90,20 @@
|
|
|
90
90
|
"express-fileupload": "^1.5.1",
|
|
91
91
|
"fast-check": "^4.1.1",
|
|
92
92
|
"helmet": "^8.1.0",
|
|
93
|
-
"hono": "^4.7.
|
|
93
|
+
"hono": "^4.7.10",
|
|
94
94
|
"inversify": "^7.5.1",
|
|
95
|
-
"jose": "^6.0.
|
|
95
|
+
"jose": "^6.0.11",
|
|
96
96
|
"knex": "^3.1.0",
|
|
97
|
-
"next": "^15.3.
|
|
98
|
-
"pg": "^8.
|
|
97
|
+
"next": "^15.3.2",
|
|
98
|
+
"pg": "^8.16.0",
|
|
99
99
|
"react": "^19.1.0",
|
|
100
100
|
"react-dom": "^19.1.0",
|
|
101
|
-
"react-native": "^0.79.
|
|
101
|
+
"react-native": "^0.79.2",
|
|
102
102
|
"reflect-metadata": "^0.2.2",
|
|
103
103
|
"sqlite3": "^5.1.7",
|
|
104
104
|
"typescript": "^5.8.3",
|
|
105
|
-
"vite": "^6.3.
|
|
106
|
-
"vitest": "^3.1.
|
|
105
|
+
"vite": "^6.3.5",
|
|
106
|
+
"vitest": "^3.1.3"
|
|
107
107
|
},
|
|
108
108
|
"peerDependenciesMeta": {
|
|
109
109
|
"@hono/node-server": {
|