@nan0web/ui 1.12.3 → 3.1.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.
Files changed (135) hide show
  1. package/package.json +29 -20
  2. package/src/Component/index.js +1 -5
  3. package/src/Model/Element.js +183 -0
  4. package/src/Theme/AppTheme.js +19 -0
  5. package/src/Theme/CustomTheme.js +32 -0
  6. package/src/Theme/DarkLightTheme.js +34 -0
  7. package/src/Theme/Theme.js +25 -0
  8. package/src/Theme/atoms/Avatar.js +20 -0
  9. package/src/Theme/atoms/Badge.js +28 -0
  10. package/src/Theme/atoms/Button.js +88 -0
  11. package/src/Theme/atoms/Checkbox.js +26 -0
  12. package/src/Theme/atoms/Input.js +28 -0
  13. package/src/Theme/atoms/Radio.js +26 -0
  14. package/src/Theme/atoms/Select.js +16 -0
  15. package/src/Theme/atoms/TextArea.js +17 -0
  16. package/src/Theme/atoms/Typography.js +26 -0
  17. package/src/Theme/atoms/index.js +11 -0
  18. package/src/Theme/createTheme.js +22 -0
  19. package/src/Theme/index.js +20 -0
  20. package/src/Theme/molecules/Card.js +24 -0
  21. package/src/Theme/molecules/index.js +3 -0
  22. package/src/Theme/organisms/Modal.js +24 -0
  23. package/src/Theme/organisms/index.js +3 -0
  24. package/src/Theme/presets/HighContrastTheme.js +65 -0
  25. package/src/Theme/presets/NightTheme.js +66 -0
  26. package/src/Theme/presets/index.js +4 -0
  27. package/src/Theme/tokens.js +115 -0
  28. package/src/core/InputAdapter.js +1 -2
  29. package/src/core/Intent.js +22 -8
  30. package/src/core/Message/Message.js +3 -0
  31. package/src/core/OutputAdapter.js +9 -13
  32. package/src/core/index.js +7 -4
  33. package/src/domain/app/IntentAuditor.js +53 -0
  34. package/src/domain/app/JsIntentAuditor.js +145 -0
  35. package/src/domain/app/PyIntentAuditor.js +144 -0
  36. package/src/domain/app/SnapshotAuditor.js +8 -8
  37. package/src/domain/components/ShellModel.js +2 -2
  38. package/src/index.js +35 -9
  39. package/src/inspect.js +3 -0
  40. package/src/utils/format.js +21 -0
  41. package/src/utils/processI18n.js +27 -0
  42. package/src/utils/resolveContext.js +79 -0
  43. package/types/Component/index.d.ts +1 -5
  44. package/types/Model/Element.d.ts +87 -0
  45. package/types/Theme/AppTheme.d.ts +14 -0
  46. package/types/Theme/CustomTheme.d.ts +21 -0
  47. package/types/Theme/DarkLightTheme.d.ts +16 -0
  48. package/types/Theme/Theme.d.ts +18 -0
  49. package/types/Theme/atoms/Avatar.d.ts +14 -0
  50. package/types/Theme/atoms/Badge.d.ts +22 -0
  51. package/types/Theme/atoms/Button.d.ts +144 -0
  52. package/types/Theme/atoms/Checkbox.d.ts +20 -0
  53. package/types/Theme/atoms/Input.d.ts +22 -0
  54. package/types/Theme/atoms/Radio.d.ts +20 -0
  55. package/types/Theme/atoms/Select.d.ts +15 -0
  56. package/types/Theme/atoms/TextArea.d.ts +17 -0
  57. package/types/Theme/atoms/Typography.d.ts +47 -0
  58. package/types/Theme/atoms/index.d.ts +10 -0
  59. package/types/Theme/createTheme.d.ts +7 -0
  60. package/types/Theme/index.d.ts +10 -0
  61. package/types/Theme/molecules/Card.d.ts +18 -0
  62. package/types/Theme/molecules/index.d.ts +2 -0
  63. package/types/Theme/organisms/Modal.d.ts +18 -0
  64. package/types/Theme/organisms/index.d.ts +2 -0
  65. package/types/Theme/presets/HighContrastTheme.d.ts +2 -0
  66. package/types/Theme/presets/NightTheme.d.ts +2 -0
  67. package/types/Theme/presets/index.d.ts +3 -0
  68. package/types/Theme/tokens.d.ts +119 -0
  69. package/types/core/Intent.d.ts +10 -7
  70. package/types/core/Message/Message.d.ts +3 -0
  71. package/types/core/OutputAdapter.d.ts +2 -4
  72. package/types/core/index.d.ts +5 -2
  73. package/types/domain/Document.d.ts +2 -1
  74. package/types/domain/FooterModel.d.ts +2 -1
  75. package/types/domain/ModelAsApp.d.ts +1 -1
  76. package/types/domain/app/IntentAuditor.d.ts +23 -0
  77. package/types/domain/app/JsIntentAuditor.d.ts +22 -0
  78. package/types/domain/app/PyIntentAuditor.d.ts +22 -0
  79. package/types/domain/app/SnapshotAuditor.d.ts +5 -6
  80. package/types/domain/components/ShellModel.d.ts +1 -5
  81. package/types/index.d.ts +7 -9
  82. package/types/inspect.d.ts +3 -0
  83. package/types/utils/format.d.ts +5 -0
  84. package/types/utils/processI18n.d.ts +8 -0
  85. package/types/utils/resolveContext.d.ts +21 -0
  86. package/src/App/Command/DepsCommand.js +0 -24
  87. package/src/App/Core/CoreApp.js +0 -125
  88. package/src/App/Core/UI.js +0 -63
  89. package/src/App/Core/Widget.js +0 -61
  90. package/src/App/Core/index.js +0 -11
  91. package/src/App/Scenario.js +0 -45
  92. package/src/App/User/Command/Message.js +0 -3
  93. package/src/App/User/Command/index.js +0 -5
  94. package/src/App/User/UserApp.js +0 -85
  95. package/src/App/User/UserUI.js +0 -20
  96. package/src/App/User/index.js +0 -9
  97. package/src/App/index.js +0 -14
  98. package/src/Component/Process/Input.js +0 -63
  99. package/src/Component/Process/Process.js +0 -24
  100. package/src/Component/Process/index.js +0 -5
  101. package/src/Component/Welcome/Input.js +0 -48
  102. package/src/Component/Welcome/Welcome.js +0 -22
  103. package/src/Component/Welcome/index.js +0 -5
  104. package/src/Frame/Frame.js +0 -608
  105. package/src/Frame/Props.js +0 -96
  106. package/src/StdIn.js +0 -100
  107. package/src/StdOut.js +0 -95
  108. package/src/View/RenderOptions.js +0 -48
  109. package/src/View/View.js +0 -306
  110. package/src/core/Message/index.js +0 -6
  111. package/types/App/Command/DepsCommand.d.ts +0 -14
  112. package/types/App/Core/CoreApp.d.ts +0 -70
  113. package/types/App/Core/UI.d.ts +0 -38
  114. package/types/App/Core/Widget.d.ts +0 -39
  115. package/types/App/Core/index.d.ts +0 -10
  116. package/types/App/Scenario.d.ts +0 -26
  117. package/types/App/User/Command/Message.d.ts +0 -2
  118. package/types/App/User/Command/index.d.ts +0 -3
  119. package/types/App/User/UserApp.d.ts +0 -41
  120. package/types/App/User/UserUI.d.ts +0 -9
  121. package/types/App/User/index.d.ts +0 -8
  122. package/types/App/index.d.ts +0 -12
  123. package/types/Component/Process/Input.d.ts +0 -48
  124. package/types/Component/Process/Process.d.ts +0 -13
  125. package/types/Component/Process/index.d.ts +0 -4
  126. package/types/Component/Welcome/Input.d.ts +0 -34
  127. package/types/Component/Welcome/Welcome.d.ts +0 -13
  128. package/types/Component/Welcome/index.d.ts +0 -4
  129. package/types/Frame/Frame.d.ts +0 -186
  130. package/types/Frame/Props.d.ts +0 -77
  131. package/types/StdIn.d.ts +0 -62
  132. package/types/StdOut.d.ts +0 -52
  133. package/types/View/RenderOptions.d.ts +0 -29
  134. package/types/View/View.d.ts +0 -124
  135. package/types/core/Message/index.d.ts +0 -4
@@ -1,14 +1,17 @@
1
1
  export { default as InputAdapter } from "./InputAdapter.js";
2
2
  export { default as OutputAdapter } from "./OutputAdapter.js";
3
+ export { default as FormInput } from "./Form/Input.js";
3
4
  export { default as UiMessage } from "./Message/Message.js";
4
5
  export { default as FormMessage } from "./Form/Message.js";
5
- export { default as FormInput } from "./Form/Input.js";
6
- export { default as UiAdapter } from "./UiAdapter.js";
6
+ export { default as OutputMessage } from "./Message/OutputMessage.js";
7
7
  export { IntentErrorModel } from "./IntentErrorModel.js";
8
8
  export { runGenerator } from "./GeneratorRunner.js";
9
9
  export { MaskHandler } from "./MaskHandler.js";
10
10
  export { LayoutModel } from "../domain/LayoutModel.js";
11
11
  export type Intent = import("./Intent.js").Intent;
12
+ export type ShowLevel = import("./Intent.js").ShowLevel;
13
+ export type ShowIntent = import("./Intent.js").ShowIntent;
14
+ export type ShowData = import("./Intent.js").ShowData;
12
15
  export type AskResponse = import("./Intent.js").AskResponse;
13
16
  export type AskOptions = import("./InputAdapter.js").AskOptions;
14
17
  import UIStream from './Stream.js';
@@ -20,7 +20,7 @@ export class Document extends Model {
20
20
  };
21
21
  static langs: {
22
22
  type: string;
23
- model: any;
23
+ model: typeof Language;
24
24
  help: string;
25
25
  };
26
26
  /**
@@ -38,3 +38,4 @@ export class Document extends Model {
38
38
  import { Model } from '@nan0web/types';
39
39
  import { Content } from './Content.js';
40
40
  import Navigation from './Navigation.js';
41
+ import { Language } from '@nan0web/i18n';
@@ -38,7 +38,7 @@ export class FooterModel extends Model {
38
38
  static langs: {
39
39
  help: string;
40
40
  type: string;
41
- hint: any;
41
+ hint: typeof Language;
42
42
  default: never[];
43
43
  };
44
44
  /**
@@ -60,3 +60,4 @@ export class FooterModel extends Model {
60
60
  }
61
61
  import { Model } from '@nan0web/types';
62
62
  import Navigation from './Navigation.js';
63
+ import { Language } from '@nan0web/i18n';
@@ -33,7 +33,7 @@ export class ModelAsApp extends Model {
33
33
  _isExplicit: boolean;
34
34
  db: import("@nan0web/db").default | null | undefined;
35
35
  plugins: Record<string, any>;
36
- t: import("@nan0web/types/src/utils/TFunction").TFunction;
36
+ t: import("../../../types/types/utils/TFunction.js").TFunction;
37
37
  };
38
38
  /**
39
39
  * Instantiates a subcommand if the value matches one of the options.
@@ -0,0 +1,23 @@
1
+ /**
2
+ * IntentAuditor — Base model for OLMUI Intent hygiene audits.
3
+ * Polymorphically delegates execution to JS or Python subclasses.
4
+ */
5
+ export class IntentAuditor extends AuditorModel {
6
+ static UI: {
7
+ title: string;
8
+ description: string;
9
+ icon: string;
10
+ starting: string;
11
+ auditPassed: string;
12
+ auditFailed: string;
13
+ doneSuccess: string;
14
+ doneErrors: string;
15
+ errorDb: string;
16
+ errorConsoleLeak: string;
17
+ errorProcessLeak: string;
18
+ errorPrintLeak: string;
19
+ errorSysWriteLeak: string;
20
+ };
21
+ }
22
+ export default IntentAuditor;
23
+ import { AuditorModel } from '@nan0web/inspect/domain/AuditorModel';
@@ -0,0 +1,22 @@
1
+ /**
2
+ * JsIntentAuditor — Specialized auditor for JS/TS output hygiene.
3
+ */
4
+ export class JsIntentAuditor extends AuditorModel {
5
+ /** @type {string[]} Directories to ignore during scanning */
6
+ static IGNORE_DIRS: string[];
7
+ /**
8
+ * Checks if a directory or file should be ignored.
9
+ * @param {string} name
10
+ * @returns {boolean}
11
+ */
12
+ static isIgnored(name: string): boolean;
13
+ /**
14
+ * Inspects file content for console.* or process.* writes.
15
+ * @param {string} content Content of the file.
16
+ * @param {import('@nan0web/i18n').TFunction} t Translate function.
17
+ * @returns {string[]} List of error messages.
18
+ */
19
+ static inspectFileContent(content: string, t: import("@nan0web/i18n").TFunction): string[];
20
+ }
21
+ export default JsIntentAuditor;
22
+ import { AuditorModel } from '@nan0web/inspect/domain/AuditorModel';
@@ -0,0 +1,22 @@
1
+ /**
2
+ * PyIntentAuditor — Specialized auditor for Python output hygiene.
3
+ */
4
+ export class PyIntentAuditor extends AuditorModel {
5
+ /** @type {string[]} Directories to ignore during scanning */
6
+ static IGNORE_DIRS: string[];
7
+ /**
8
+ * Checks if a directory or file should be ignored.
9
+ * @param {string} name
10
+ * @returns {boolean}
11
+ */
12
+ static isIgnored(name: string): boolean;
13
+ /**
14
+ * Inspects Python file content for print or sys.stdout/stderr writes.
15
+ * @param {string} content Content of the file.
16
+ * @param {import('@nan0web/i18n').TFunction} t Translate function.
17
+ * @returns {string[]} List of error messages.
18
+ */
19
+ static inspectFileContent(content: string, t: import("@nan0web/i18n").TFunction): string[];
20
+ }
21
+ export default PyIntentAuditor;
22
+ import { AuditorModel } from '@nan0web/inspect/domain/AuditorModel';
@@ -3,7 +3,6 @@
3
3
  * Parses snapshots without evaluating the app logic and detects artifacts.
4
4
  */
5
5
  export class SnapshotAuditor extends AuditorModel {
6
- static alias: string;
7
6
  static data: {
8
7
  type: string;
9
8
  help: string;
@@ -56,11 +55,11 @@ export class SnapshotAuditor extends AuditorModel {
56
55
  static extractWords(obj: any, set: Set<string>): void;
57
56
  /**
58
57
  * Scans data directories to build a word set for each language.
59
- * @param {import('@nan0web/db').DB} fsDb FileSystem DB.
58
+ * @param {any} fsDb FileSystem DB.
60
59
  * @param {string} data
61
60
  * @returns {Promise<Record<string, Set<string>>>}
62
61
  */
63
- static buildDictionaries(fsDb: import("@nan0web/db").DB, data?: string): Promise<Record<string, Set<string>>>;
62
+ static buildDictionaries(fsDb: any, data?: string): Promise<Record<string, Set<string>>>;
64
63
  /**
65
64
  * Inspects a single snapshot text.
66
65
  * @param {string} content Content of the file.
@@ -70,7 +69,7 @@ export class SnapshotAuditor extends AuditorModel {
70
69
  * @param {Record<string, Set<string>>} [dictionaries=undefined] Loaded dictionaries for mutual exclusion check.
71
70
  * @returns {{ score: number, errors: string[] }}
72
71
  */
73
- static inspectText(content: string, locale: string, filename: string, t: any, dictionaries?: Record<string, Set<string>>): {
72
+ static inspectText(content: string, locale: string, filename: string, t: import("@nan0web/i18n").TFunction, dictionaries?: Record<string, Set<string>>): {
74
73
  score: number;
75
74
  errors: string[];
76
75
  };
@@ -83,7 +82,7 @@ export class SnapshotAuditor extends AuditorModel {
83
82
  static checkNode(node: any, path: string, context: {
84
83
  locale: string;
85
84
  errors: string[];
86
- t: any;
85
+ t: import("@nan0web/i18n").TFunction;
87
86
  dictionaries?: Record<string, Set<string>>;
88
87
  }): void;
89
88
  /**
@@ -95,7 +94,7 @@ export class SnapshotAuditor extends AuditorModel {
95
94
  static checkString(str: string, path: string, context: {
96
95
  locale: string;
97
96
  errors: string[];
98
- t: any;
97
+ t: import("@nan0web/i18n").TFunction;
99
98
  dictionaries?: Record<string, Set<string>>;
100
99
  }): void;
101
100
  /**
@@ -46,11 +46,7 @@ export class ShellModel extends Model {
46
46
  /** @type {string} */ index: string;
47
47
  /** @type {string} */ locale: string;
48
48
  /** @type {string} */ port: string;
49
- run(): AsyncGenerator<import("../../core/Intent.js").AskIntent | {
50
- type: string;
51
- level: any;
52
- message: any;
53
- }, any, unknown>;
49
+ run(): AsyncGenerator<import("../../core/Intent.js").AskIntent | import("../../core/Intent.js").LogIntent, any, unknown>;
54
50
  #private;
55
51
  }
56
52
  import { Model } from '@nan0web/types';
package/types/index.d.ts CHANGED
@@ -1,3 +1,6 @@
1
+ export { default as Element } from "./Model/Element.js";
2
+ export { resolveContext } from "./utils/resolveContext.js";
3
+ export { processI18n } from "./utils/processI18n.js";
1
4
  export { format } from "./format.js";
2
5
  export { default as Navigation } from "./domain/Navigation.js";
3
6
  export { default as FormMessage } from "./core/Form/Message.js";
@@ -7,9 +10,9 @@ export { default as OutputAdapter } from "./core/OutputAdapter.js";
7
10
  export { default as OutputMessage } from "./core/Message/OutputMessage.js";
8
11
  export { default as UiForm } from "./core/Form/Form.js";
9
12
  export { default as UiMessage } from "./core/Message/Message.js";
10
- export { default as UiStream } from "./core/Stream.js";
11
13
  export { default as UiAdapter } from "./core/UiAdapter.js";
12
14
  export { resolvePositionalArgs } from "./core/resolvePositionalArgs.js";
15
+ export { tokens } from "./Theme/tokens.js";
13
16
  export * from "./core/Intent.js";
14
17
  export * from "./domain/index.js";
15
18
  export { IntentErrorModel } from "./core/IntentErrorModel.js";
@@ -33,16 +36,11 @@ export type AbortResponse = import("./core/Intent.js").AbortResponse;
33
36
  export type ShowData = import("./core/Intent.js").ShowData;
34
37
  export type AskOptions = import("./core/InputAdapter.js").AskOptions;
35
38
  export type ModelAsAppOptions = import("./domain/index.js").ModelAsAppOptions;
36
- import Frame from './Frame/Frame.js';
37
- import FrameProps from './Frame/Props.js';
38
39
  import Locale from './Locale.js';
39
- import StdIn from './StdIn.js';
40
- import StdOut from './StdOut.js';
41
- import View from './View/View.js';
42
- import RenderOptions from './View/RenderOptions.js';
43
40
  import { Model } from '@nan0web/types';
44
41
  import Models from './Model/index.js';
45
42
  import Component from './Component/index.js';
46
- import App from './App/index.js';
47
- export { Frame, FrameProps, Locale, StdIn, StdOut, View, RenderOptions, Model, Models, Component, App };
43
+ export { Locale, Model, Models, Component };
44
+ export { default as Theme, getUserTheme, CustomTheme, DarkLightTheme, NightTheme, createTheme } from "./Theme/index.js";
48
45
  export { default as Error, CancelError } from "./core/Error/index.js";
46
+ export { runFlow, flow, Prompt, Stream, Alert, Toast, Badge, Text, Table, Input, Select, Confirm, Multiselect, Mask, Password, Spinner, Progress, default as Flow } from "./core/Flow.js";
@@ -1,3 +1,6 @@
1
1
  export * from "./testing/SnapshotRunner.js";
2
2
  export * from "./testing/verifySnapshot.js";
3
3
  export { SnapshotAuditor } from "./domain/app/SnapshotAuditor.js";
4
+ export { IntentAuditor } from "./domain/app/IntentAuditor.js";
5
+ export { JsIntentAuditor } from "./domain/app/JsIntentAuditor.js";
6
+ export { PyIntentAuditor } from "./domain/app/PyIntentAuditor.js";
@@ -0,0 +1,5 @@
1
+ export namespace format {
2
+ function currency(value: any, currency?: string, locale?: string): string;
3
+ function rate(value: any, locale?: string): string;
4
+ function number(value: any, locale?: string): string;
5
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Обробляє i18n та замінює змінні в тексті.
3
+ * @param {any} input - Вміст для обробки.
4
+ * @param {Function} [t] - Функція перекладу.
5
+ * @param {Object} [data] - Дані для підстановки.
6
+ * @returns {any}
7
+ */
8
+ export function processI18n(input: any, t?: Function, data?: any): any;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Resolves context-aware values in strings and objects.
3
+ * Supports:
4
+ * - References: `data:currencies`, `action:save`, `fn:calculate,100,USD,UAH`
5
+ * - Templates: `{{user.name}}`, `{{currencies[0]}}`
6
+ * - Scalars: numbers, booleans, etc. (passthrough)
7
+ *
8
+ * @param {Object} context - Application context (data, actions, functions)
9
+ * @param {*} value - Raw value to resolve
10
+ * @returns {*} Resolved value
11
+ */
12
+ export function resolveContext(context: any, value: any): any;
13
+ /**
14
+ * Resolves a context value or returns undefined if resolution fails.
15
+ * Used internally by renderers to safely resolve props.
16
+ *
17
+ * @param {Object} ctx - Context with data, actions and functions
18
+ * @param {string} value - String value to resolve
19
+ * @returns {*} Resolved value or undefined
20
+ */
21
+ export function resolveContextValue(ctx: any, value: string): any;
@@ -1,24 +0,0 @@
1
- import UiMessage from '../../core/Message/Message.js'
2
-
3
- class DepsCommandBody {
4
- static fix = {
5
- help: 'Fix dependencies',
6
- defaultValue: false,
7
- }
8
- constructor(input = {}) {
9
- const { fix = DepsCommandBody.fix.defaultValue } = input
10
- this.fix = !!fix
11
- }
12
- }
13
-
14
- export class DepsCommand extends UiMessage {
15
- static Body = DepsCommandBody
16
- constructor(input = {}) {
17
- const { body = new DepsCommandBody() } = UiMessage.parseBody(input, DepsCommandBody)
18
- super(input)
19
- /** @type {DepsCommandBody} */
20
- this.body = body
21
- }
22
- }
23
-
24
- export default DepsCommand
@@ -1,125 +0,0 @@
1
- import { Message } from '@nan0web/co'
2
- import { typeOf } from '@nan0web/types'
3
- import UI from './UI.js'
4
-
5
- /** @typedef {Function} CommandFn */
6
-
7
- /**
8
- * Abstract base class for all apps.
9
- * Each app processes input commands and produces output.
10
- */
11
- export default class CoreApp {
12
- /** @type {string} App name */
13
- name
14
-
15
- /** @type {Map<string, CommandFn>} Registered command handlers */
16
- commands
17
-
18
- /** @type {Record<string, any>} App state */
19
- state
20
-
21
- /** @type {Message} Starting command parsed from argv */
22
- startCommand
23
-
24
- /**
25
- * Creates a new CoreApp instance.
26
- * @param {object} props - CoreApp properties
27
- * @param {string} [props.name="CoreApp"] - App name
28
- * @param {object} [props.state={}] - Initial state object
29
- * @param {Message} [props.startCommand=new Message()] - Command line arguments to parse
30
- */
31
- constructor(props = {}) {
32
- const { name = 'CoreApp', state = {}, startCommand = new Message() } = props
33
- this.name = String(name)
34
- this.state = state
35
- this.commands = new Map()
36
- // @deprecated @todo fix the argv by moving to ui-cli.
37
- this.startCommand = Message.from(startCommand ?? {})
38
- }
39
-
40
- /**
41
- * Sets app state.
42
- * @param {string|object} state - State key or object with multiple keys
43
- * @param {any} [value] - State value if state is a string key
44
- * @returns {object} Updated state
45
- */
46
- set(state, value) {
47
- if ('string' === typeof state) {
48
- this.state[state] = value
49
- } else {
50
- Object.assign(this.state, state)
51
- }
52
- // @todo save state
53
- return this.state
54
- }
55
-
56
- /**
57
- * Register a command handler.
58
- * @param {string} commandName - Name of the command to register
59
- * @param {Function} handler - async function or sync function that accepts params and returns output
60
- */
61
- registerCommand(commandName, handler) {
62
- if (!typeOf(Function)(handler)) {
63
- throw new TypeError('Handler must be a function')
64
- }
65
- this.commands.set(commandName, handler)
66
- }
67
-
68
- /**
69
- * Returns a string representation of the app.
70
- * @returns {string} String representation including name and state
71
- */
72
- toString() {
73
- return `${this.constructor.name}(name=${this.name}, state=${JSON.stringify(this.state)})`
74
- }
75
-
76
- /**
77
- * Process a command message.
78
- * @param {Message} msg - Command to process
79
- * @param {UI} ui - UI instance to use for rendering
80
- * @returns {Promise<any>} Output of the command
81
- * @throws {Error} If the command is not registered
82
- */
83
- async processCommand(msg, ui) {
84
- const handler = this.commands.get(msg.constructor.name)
85
- if (!handler) {
86
- throw new Error(
87
- [
88
- 'Unknown command',
89
- ': ',
90
- msg.constructor.name,
91
- '\n',
92
- 'Available commands',
93
- ': ',
94
- [...this.commands.keys()].join(', '),
95
- ].join(''),
96
- )
97
- }
98
- return await handler.apply(this, [msg, ui])
99
- }
100
-
101
- /**
102
- * Process an array of command messages sequentially.
103
- * @param {Message[]} Messages - Array of commands to process
104
- * @param {UI} ui - UI instance to use for rendering
105
- * @returns {Promise<any[]>} Array of command outputs
106
- */
107
- async processCommands(Messages, ui) {
108
- const results = []
109
- for (const cmdMsg of Messages) {
110
- const result = await this.processCommand(cmdMsg, ui)
111
- results.push(result)
112
- }
113
- return results
114
- }
115
-
116
- /**
117
- * Select a command to run. Must be implemented by subclasses.
118
- * @param {UI} ui - UI instance for interaction
119
- * @returns {Promise<string>} Command name to execute
120
- * @throws {Error} Always thrown as this method must be implemented by subclasses
121
- */
122
- async selectCommand(ui) {
123
- throw new Error('Not implemented, must be implemented by subclass')
124
- }
125
- }
@@ -1,63 +0,0 @@
1
- import { Message } from '@nan0web/co'
2
- import View from '../../View/View.js'
3
- import CoreApp from './CoreApp.js'
4
- import Widget from './Widget.js'
5
-
6
- /** @typedef {import("../../View/View.js").ComponentFn} ComponentFn */
7
-
8
- /**
9
- * Abstract UI class to connect apps and widgets.
10
- * Supports input/output data typed classes and views.
11
- */
12
- export default class UI extends Widget {
13
- /** @type {CoreApp} The app instance connected to this UI */
14
- app
15
-
16
- /**
17
- * Creates a new UI instance.
18
- * @param {CoreApp} app - The app to connect to this UI
19
- * @param {View} [view] - View instance for rendering (default: new View())
20
- */
21
- constructor(app, view = new View()) {
22
- super(view)
23
- this.app = app
24
- }
25
-
26
- /**
27
- * Convert raw input to Message array.
28
- * Must be implemented by subclasses.
29
- * @param {any} rawInput - Raw input to convert
30
- * @returns {Message[]} Array of command messages
31
- * @throws {Error} Always thrown as this method must be implemented by subclasses
32
- */
33
- convertInput(rawInput) {
34
- throw new Error('convertInput must be implemented by subclass')
35
- }
36
-
37
- /**
38
- * Sets up event handlers for UI process events.
39
- * @param {ComponentFn} UIProcess - Process view component
40
- */
41
- show(UIProcess) {
42
- if (!UIProcess) return
43
- const onStart = () => {
44
- // this.view.render(UIProcess)
45
- }
46
- const onData = () => {}
47
- const onEnd = () => {}
48
-
49
- this.on('start', onStart)
50
- this.on('data', onData)
51
- this.on('end', onEnd)
52
- }
53
-
54
- /**
55
- * Output results to the interface.
56
- * @param {any[]} results - Results to output
57
- */
58
- output(results) {
59
- results.forEach((result) => {
60
- this.view.info(JSON.stringify(result))
61
- })
62
- }
63
- }
@@ -1,61 +0,0 @@
1
- import EventProcessor from '@nan0web/event/oop'
2
- import View from '../../View/View.js'
3
- import { StreamEntry } from '@nan0web/db'
4
- import { UiMessage } from '../../core/index.js'
5
-
6
- /** @typedef {import("./UI.js").ComponentFn} ComponentFn */
7
-
8
- /**
9
- * Abstract Widget class.
10
- * Widget is a view with ability to input data in a specific format.
11
- * Input and output data are typed classes.
12
- */
13
- export default class Widget extends EventProcessor {
14
- /** @type {View} The view associated with this widget */
15
- view
16
-
17
- /**
18
- * Creates a new Widget instance.
19
- * @param {View} [view] - View instance (default: new View())
20
- */
21
- constructor(view = new View()) {
22
- super()
23
- this.view = view
24
- }
25
-
26
- /**
27
- * Ask user for input data of specific class.
28
- * @param {UiMessage} input - instance of UiMessage or similar
29
- * @returns {Promise<UiMessage | null>} instance of UiMessage or null
30
- */
31
- async ask(input) {
32
- return await this.view.ask(input)
33
- }
34
-
35
- /**
36
- * @param {AsyncGenerator<StreamEntry>} stream
37
- * @returns {Promise<void>}
38
- */
39
- async read(stream) {
40
- for await (const entry of stream) {
41
- this.view.progress(true)(entry)
42
- }
43
- }
44
-
45
- /**
46
- * Render output data using a view function.
47
- * @param {Function|string} viewFnOrName - View function or registered view name
48
- * @param {object} outputData - Typed output data instance
49
- * @returns {any} Rendered output
50
- * @throws {Error} If view component is not found when using string name
51
- */
52
- render(viewFnOrName, outputData) {
53
- /** @type {Function | ComponentFn | undefined} */
54
- const viewFn = typeof viewFnOrName === 'string' ? this.view.get(viewFnOrName) : viewFnOrName
55
-
56
- if (!viewFn) {
57
- throw new Error(['View component not found', ': ', viewFnOrName].join(''))
58
- }
59
- return this.view.render(viewFn)(outputData)
60
- }
61
- }
@@ -1,11 +0,0 @@
1
- import CoreApp from './CoreApp.js'
2
- import UI from './UI.js'
3
- import Widget from './Widget.js'
4
-
5
- export { CoreApp, UI }
6
-
7
- export default {
8
- App: CoreApp,
9
- UI,
10
- Widget,
11
- }
@@ -1,45 +0,0 @@
1
- import App from './index.js'
2
- import UI from './Core/UI.js'
3
-
4
- /** @typedef {import("./Core/CoreApp.js").default} CoreApp */
5
-
6
- /**
7
- * Abstract Scenario class to test app logic.
8
- * Scenarios run input commands and verify output.
9
- */
10
- export default class Scenario {
11
- /** @type {CoreApp} The app to run scenarios against */
12
- app
13
-
14
- /**
15
- * Creates a new Scenario instance.
16
- * @param {CoreApp} app - App instance to run scenarios against
17
- * @param {UI} ui - User interface
18
- * @throws {TypeError} If app is not an App.Core.App instance
19
- */
20
- constructor(app, ui) {
21
- if (!(app instanceof App.Core.App)) {
22
- throw new TypeError('Scenario requires a App.Core.App instance')
23
- }
24
- this.app = app
25
- this.ui = ui
26
- }
27
-
28
- /**
29
- * Run scenario with input commands and expected output.
30
- * @param {Array<any[]>} inputCommands - Array of command arrays
31
- * @param {Array<any>} expectedOutputs - Expected outputs for each command
32
- * @returns {Promise<boolean>} True if all outputs match expected
33
- */
34
- async run(inputCommands, expectedOutputs) {
35
- const commandMessages = inputCommands.map((arr) => App.Command.Message.parse(arr))
36
- const outputs = await this.app.processCommands(commandMessages, this.ui)
37
- if (outputs.length !== expectedOutputs.length) return false
38
- for (let i = 0; i < outputs.length; i++) {
39
- if (JSON.stringify(outputs[i]) !== JSON.stringify(expectedOutputs[i])) {
40
- return false
41
- }
42
- }
43
- return true
44
- }
45
- }
@@ -1,3 +0,0 @@
1
- import UiMessage from '../../../core/Message/Message.js'
2
-
3
- export default UiMessage
@@ -1,5 +0,0 @@
1
- import CommandMessage from './Message.js'
2
-
3
- export { CommandMessage }
4
-
5
- export default CommandMessage