@aerogel/core 0.0.0-next.c4825c5cbe0fe3257e478c2a7ec8df27d5a72305 → 0.0.0-next.d197d66a9d339318d752a1d8a96b2919faba3003

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 (194) hide show
  1. package/dist/aerogel-core.d.ts +1873 -1949
  2. package/dist/aerogel-core.js +3268 -0
  3. package/dist/aerogel-core.js.map +1 -0
  4. package/package.json +31 -37
  5. package/src/bootstrap/bootstrap.test.ts +4 -7
  6. package/src/bootstrap/index.ts +14 -15
  7. package/src/bootstrap/options.ts +1 -1
  8. package/src/components/AppLayout.vue +14 -0
  9. package/src/components/{AGAppModals.vue → AppModals.vue} +3 -4
  10. package/src/components/AppOverlays.vue +9 -0
  11. package/src/components/AppToasts.vue +16 -0
  12. package/src/components/contracts/AlertModal.ts +8 -0
  13. package/src/components/contracts/Button.ts +16 -0
  14. package/src/components/contracts/ConfirmModal.ts +46 -0
  15. package/src/components/contracts/DropdownMenu.ts +20 -0
  16. package/src/components/contracts/ErrorReportModal.ts +32 -0
  17. package/src/components/contracts/Input.ts +26 -0
  18. package/src/components/contracts/LoadingModal.ts +26 -0
  19. package/src/components/contracts/Modal.ts +21 -0
  20. package/src/components/contracts/PromptModal.ts +34 -0
  21. package/src/components/contracts/Select.ts +45 -0
  22. package/src/components/contracts/Toast.ts +15 -0
  23. package/src/components/contracts/index.ts +11 -0
  24. package/src/components/headless/HeadlessButton.vue +51 -0
  25. package/src/components/headless/HeadlessInput.vue +59 -0
  26. package/src/components/headless/{forms/AGHeadlessInputDescription.vue → HeadlessInputDescription.vue} +7 -8
  27. package/src/components/headless/{forms/AGHeadlessInputError.vue → HeadlessInputError.vue} +4 -8
  28. package/src/components/headless/{forms/AGHeadlessInputInput.vue → HeadlessInputInput.vue} +16 -25
  29. package/src/components/headless/{forms/AGHeadlessInputLabel.vue → HeadlessInputLabel.vue} +3 -7
  30. package/src/components/headless/{forms/AGHeadlessInputTextArea.vue → HeadlessInputTextArea.vue} +10 -13
  31. package/src/components/headless/HeadlessModal.vue +57 -0
  32. package/src/components/headless/HeadlessModalContent.vue +30 -0
  33. package/src/components/headless/HeadlessModalDescription.vue +12 -0
  34. package/src/components/headless/HeadlessModalOverlay.vue +12 -0
  35. package/src/components/headless/HeadlessModalTitle.vue +12 -0
  36. package/src/components/headless/HeadlessSelect.vue +120 -0
  37. package/src/components/headless/{forms/AGHeadlessSelectError.vue → HeadlessSelectError.vue} +5 -6
  38. package/src/components/headless/HeadlessSelectLabel.vue +25 -0
  39. package/src/components/headless/HeadlessSelectOption.vue +34 -0
  40. package/src/components/headless/HeadlessSelectOptions.vue +42 -0
  41. package/src/components/headless/HeadlessSelectTrigger.vue +22 -0
  42. package/src/components/headless/HeadlessSelectValue.vue +18 -0
  43. package/src/components/headless/HeadlessToast.vue +18 -0
  44. package/src/components/headless/HeadlessToastAction.vue +13 -0
  45. package/src/components/headless/index.ts +19 -3
  46. package/src/components/index.ts +6 -11
  47. package/src/components/ui/AdvancedOptions.vue +18 -0
  48. package/src/components/ui/AlertModal.vue +14 -0
  49. package/src/components/ui/Button.vue +98 -0
  50. package/src/components/ui/Checkbox.vue +56 -0
  51. package/src/components/ui/ConfirmModal.vue +45 -0
  52. package/src/components/ui/DropdownMenu.vue +32 -0
  53. package/src/components/ui/DropdownMenuOption.vue +14 -0
  54. package/src/components/ui/DropdownMenuOptions.vue +27 -0
  55. package/src/components/ui/EditableContent.vue +82 -0
  56. package/src/components/ui/ErrorMessage.vue +15 -0
  57. package/src/components/ui/ErrorReportModal.vue +67 -0
  58. package/src/components/{modals/AGErrorReportModalButtons.vue → ui/ErrorReportModalButtons.vue} +34 -27
  59. package/src/components/ui/ErrorReportModalTitle.vue +24 -0
  60. package/src/components/{forms/AGForm.vue → ui/Form.vue} +4 -5
  61. package/src/components/ui/Input.vue +56 -0
  62. package/src/components/ui/Link.vue +12 -0
  63. package/src/components/ui/LoadingModal.vue +34 -0
  64. package/src/components/ui/Markdown.vue +69 -0
  65. package/src/components/ui/Modal.vue +122 -0
  66. package/src/components/{modals/AGModalContext.vue → ui/ModalContext.vue} +8 -9
  67. package/src/components/ui/ProgressBar.vue +51 -0
  68. package/src/components/ui/PromptModal.vue +38 -0
  69. package/src/components/ui/Select.vue +27 -0
  70. package/src/components/ui/SelectLabel.vue +17 -0
  71. package/src/components/ui/SelectOption.vue +29 -0
  72. package/src/components/ui/SelectOptions.vue +35 -0
  73. package/src/components/ui/SelectTrigger.vue +29 -0
  74. package/src/components/ui/SettingsModal.vue +15 -0
  75. package/src/components/{lib/AGStartupCrash.vue → ui/StartupCrash.vue} +8 -8
  76. package/src/components/ui/Toast.vue +44 -0
  77. package/src/components/ui/index.ts +30 -0
  78. package/src/directives/index.ts +9 -5
  79. package/src/directives/measure.ts +1 -1
  80. package/src/errors/Errors.state.ts +1 -1
  81. package/src/errors/Errors.ts +14 -23
  82. package/src/errors/index.ts +9 -6
  83. package/src/errors/utils.ts +1 -1
  84. package/src/forms/{Form.test.ts → FormController.test.ts} +32 -8
  85. package/src/forms/{Form.ts → FormController.ts} +42 -38
  86. package/src/forms/index.ts +2 -3
  87. package/src/forms/utils.ts +35 -35
  88. package/src/index.css +72 -0
  89. package/src/jobs/Job.ts +2 -2
  90. package/src/jobs/listeners.ts +1 -1
  91. package/src/lang/DefaultLangProvider.ts +7 -4
  92. package/src/lang/Lang.state.ts +1 -1
  93. package/src/lang/Lang.ts +1 -1
  94. package/src/lang/index.ts +12 -6
  95. package/src/lang/settings/Language.vue +48 -0
  96. package/src/lang/settings/index.ts +10 -0
  97. package/src/plugins/Plugin.ts +1 -1
  98. package/src/plugins/index.ts +10 -7
  99. package/src/services/App.state.ts +15 -4
  100. package/src/services/App.ts +12 -4
  101. package/src/services/Cache.ts +1 -1
  102. package/src/services/Events.test.ts +8 -8
  103. package/src/services/Events.ts +4 -10
  104. package/src/services/Service.ts +21 -21
  105. package/src/services/Storage.ts +3 -3
  106. package/src/services/index.ts +8 -4
  107. package/src/services/utils.ts +2 -2
  108. package/src/testing/index.ts +4 -3
  109. package/src/testing/setup.ts +3 -19
  110. package/src/ui/UI.state.ts +8 -13
  111. package/src/ui/UI.ts +124 -111
  112. package/src/ui/index.ts +27 -28
  113. package/src/utils/classes.ts +49 -0
  114. package/src/utils/composition/events.ts +4 -6
  115. package/src/utils/composition/forms.ts +20 -4
  116. package/src/utils/composition/state.ts +11 -2
  117. package/src/utils/index.ts +4 -1
  118. package/src/utils/markdown.ts +3 -5
  119. package/src/utils/types.ts +3 -0
  120. package/src/utils/vue.ts +25 -136
  121. package/dist/aerogel-core.cjs.js +0 -2
  122. package/dist/aerogel-core.cjs.js.map +0 -1
  123. package/dist/aerogel-core.esm.js +0 -2
  124. package/dist/aerogel-core.esm.js.map +0 -1
  125. package/histoire.config.ts +0 -7
  126. package/noeldemartin.config.js +0 -5
  127. package/postcss.config.js +0 -6
  128. package/src/assets/histoire.css +0 -3
  129. package/src/components/AGAppLayout.vue +0 -16
  130. package/src/components/AGAppOverlays.vue +0 -41
  131. package/src/components/AGAppSnackbars.vue +0 -13
  132. package/src/components/composition.ts +0 -23
  133. package/src/components/constants.ts +0 -8
  134. package/src/components/forms/AGButton.vue +0 -44
  135. package/src/components/forms/AGCheckbox.vue +0 -41
  136. package/src/components/forms/AGInput.vue +0 -40
  137. package/src/components/forms/AGSelect.story.vue +0 -46
  138. package/src/components/forms/AGSelect.vue +0 -60
  139. package/src/components/forms/index.ts +0 -5
  140. package/src/components/headless/forms/AGHeadlessButton.ts +0 -3
  141. package/src/components/headless/forms/AGHeadlessButton.vue +0 -62
  142. package/src/components/headless/forms/AGHeadlessInput.ts +0 -34
  143. package/src/components/headless/forms/AGHeadlessInput.vue +0 -70
  144. package/src/components/headless/forms/AGHeadlessSelect.ts +0 -42
  145. package/src/components/headless/forms/AGHeadlessSelect.vue +0 -77
  146. package/src/components/headless/forms/AGHeadlessSelectButton.vue +0 -24
  147. package/src/components/headless/forms/AGHeadlessSelectLabel.vue +0 -24
  148. package/src/components/headless/forms/AGHeadlessSelectOption.ts +0 -4
  149. package/src/components/headless/forms/AGHeadlessSelectOption.vue +0 -39
  150. package/src/components/headless/forms/AGHeadlessSelectOptions.ts +0 -3
  151. package/src/components/headless/forms/composition.ts +0 -10
  152. package/src/components/headless/forms/index.ts +0 -18
  153. package/src/components/headless/modals/AGHeadlessModal.ts +0 -36
  154. package/src/components/headless/modals/AGHeadlessModal.vue +0 -92
  155. package/src/components/headless/modals/AGHeadlessModalPanel.vue +0 -32
  156. package/src/components/headless/modals/AGHeadlessModalTitle.vue +0 -23
  157. package/src/components/headless/modals/index.ts +0 -4
  158. package/src/components/headless/snackbars/AGHeadlessSnackbar.vue +0 -10
  159. package/src/components/headless/snackbars/index.ts +0 -40
  160. package/src/components/interfaces.ts +0 -24
  161. package/src/components/lib/AGErrorMessage.vue +0 -16
  162. package/src/components/lib/AGLink.vue +0 -9
  163. package/src/components/lib/AGMarkdown.vue +0 -54
  164. package/src/components/lib/AGMeasured.vue +0 -16
  165. package/src/components/lib/AGProgressBar.vue +0 -30
  166. package/src/components/lib/index.ts +0 -6
  167. package/src/components/modals/AGAlertModal.ts +0 -18
  168. package/src/components/modals/AGAlertModal.vue +0 -14
  169. package/src/components/modals/AGConfirmModal.ts +0 -42
  170. package/src/components/modals/AGConfirmModal.vue +0 -26
  171. package/src/components/modals/AGErrorReportModal.ts +0 -49
  172. package/src/components/modals/AGErrorReportModal.vue +0 -54
  173. package/src/components/modals/AGErrorReportModalTitle.vue +0 -25
  174. package/src/components/modals/AGLoadingModal.ts +0 -29
  175. package/src/components/modals/AGLoadingModal.vue +0 -15
  176. package/src/components/modals/AGModal.ts +0 -11
  177. package/src/components/modals/AGModal.vue +0 -39
  178. package/src/components/modals/AGModalContext.ts +0 -8
  179. package/src/components/modals/AGModalTitle.vue +0 -9
  180. package/src/components/modals/AGPromptModal.ts +0 -41
  181. package/src/components/modals/AGPromptModal.vue +0 -34
  182. package/src/components/modals/index.ts +0 -17
  183. package/src/components/snackbars/AGSnackbar.vue +0 -36
  184. package/src/components/snackbars/index.ts +0 -3
  185. package/src/components/utils.ts +0 -10
  186. package/src/directives/initial-focus.ts +0 -11
  187. package/src/forms/composition.ts +0 -6
  188. package/src/main.histoire.ts +0 -1
  189. package/src/utils/tailwindcss.test.ts +0 -26
  190. package/src/utils/tailwindcss.ts +0 -7
  191. package/tailwind.config.js +0 -4
  192. package/tsconfig.json +0 -11
  193. package/vite.config.ts +0 -17
  194. /package/src/{main.ts → index.ts} +0 -0
package/src/jobs/Job.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { ListenersManager, PromisedValue, round, tap, toError } from '@noeldemartin/utils';
2
2
  import type { Listeners } from '@noeldemartin/utils';
3
3
 
4
- import JobCancelledError from '@/errors/JobCancelledError';
4
+ import JobCancelledError from '@aerogel/core/errors/JobCancelledError';
5
5
 
6
6
  import type { JobListener } from './listeners';
7
7
  import type { JobStatus } from './status';
@@ -9,7 +9,7 @@ import type { JobStatus } from './status';
9
9
  export default abstract class Job<
10
10
  Listener extends JobListener = JobListener,
11
11
  Status extends JobStatus = JobStatus,
12
- SerializedStatus extends JobStatus = JobStatus
12
+ SerializedStatus extends JobStatus = JobStatus,
13
13
  > {
14
14
 
15
15
  protected status: Status;
@@ -1,3 +1,3 @@
1
1
  export interface JobListener {
2
- onUpdated(progress: number): unknown;
2
+ onUpdated?(progress: number): unknown;
3
3
  }
@@ -1,10 +1,13 @@
1
- import App from '@/services/App';
1
+ import { isDevelopment } from '@noeldemartin/utils';
2
2
 
3
3
  import type { LangProvider } from './Lang';
4
4
 
5
5
  export default class DefaultLangProvider implements LangProvider {
6
6
 
7
- constructor(private locale: string, private fallbackLocale: string) {}
7
+ constructor(
8
+ private locale: string,
9
+ private fallbackLocale: string,
10
+ ) {}
8
11
 
9
12
  public getLocale(): string {
10
13
  return this.locale;
@@ -28,14 +31,14 @@ export default class DefaultLangProvider implements LangProvider {
28
31
 
29
32
  public translate(key: string): string {
30
33
  // eslint-disable-next-line no-console
31
- App.development && console.warn('Lang provider is missing');
34
+ isDevelopment() && console.warn('Lang provider is missing');
32
35
 
33
36
  return key;
34
37
  }
35
38
 
36
39
  public translateWithDefault(_: string, defaultMessage: string): string {
37
40
  // eslint-disable-next-line no-console
38
- App.development && console.warn('Lang provider is missing');
41
+ isDevelopment() && console.warn('Lang provider is missing');
39
42
 
40
43
  return defaultMessage;
41
44
  }
@@ -1,4 +1,4 @@
1
- import { defineServiceState } from '@/services/Service';
1
+ import { defineServiceState } from '@aerogel/core/services/Service';
2
2
 
3
3
  export default defineServiceState({
4
4
  name: 'lang',
package/src/lang/Lang.ts CHANGED
@@ -52,7 +52,7 @@ export class LangService extends Service {
52
52
  return navigator.languages.find((locale) => locales.includes(locale)) ?? 'en';
53
53
  }
54
54
 
55
- protected async boot(): Promise<void> {
55
+ protected override async boot(): Promise<void> {
56
56
  if (!globalThis.document) {
57
57
  return;
58
58
  }
package/src/lang/index.ts CHANGED
@@ -1,10 +1,14 @@
1
- import { bootServices } from '@/services';
2
- import { definePlugin } from '@/plugins';
1
+ import App from '@aerogel/core/services/App';
2
+ import { bootServices } from '@aerogel/core/services';
3
+ import { definePlugin } from '@aerogel/core/plugins';
3
4
 
4
- import Lang, { LangProvider } from './Lang';
5
+ import Lang from './Lang';
6
+ import settings from './settings';
7
+ import type { LangProvider } from './Lang';
5
8
  import { translate, translateWithDefault } from './utils';
6
9
 
7
- export { Lang, LangProvider, translate, translateWithDefault };
10
+ export { Lang, translate, translateWithDefault };
11
+ export type { LangProvider };
8
12
 
9
13
  const services = { $lang: Lang };
10
14
 
@@ -15,15 +19,17 @@ export default definePlugin({
15
19
  app.config.globalProperties.$t ??= translate;
16
20
  app.config.globalProperties.$td = translateWithDefault;
17
21
 
22
+ settings.forEach((setting) => App.addSetting(setting));
23
+
18
24
  await bootServices(app, services);
19
25
  },
20
26
  });
21
27
 
22
- declare module '@/services' {
28
+ declare module '@aerogel/core/services' {
23
29
  export interface Services extends LangServices {}
24
30
  }
25
31
 
26
- declare module '@vue/runtime-core' {
32
+ declare module 'vue' {
27
33
  interface ComponentCustomProperties {
28
34
  $td: typeof translateWithDefault;
29
35
  }
@@ -0,0 +1,48 @@
1
+ <template>
2
+ <Select
3
+ v-model="$lang.locale"
4
+ class="flex flex-col items-start md:flex-row"
5
+ as="div"
6
+ :options
7
+ :render-option="renderLocale"
8
+ >
9
+ <div class="grow">
10
+ <SelectLabel>
11
+ {{ $td('settings.locale', 'Language') }}
12
+ </SelectLabel>
13
+ <Markdown
14
+ lang-key="settings.localeDescription"
15
+ lang-default="Choose the application's language."
16
+ class="mt-1 text-sm text-gray-500"
17
+ />
18
+ </div>
19
+ <Button variant="ghost" :as="SelectTrigger" class="grid w-auto outline-none" />
20
+ <SelectOptions />
21
+ </Select>
22
+ </template>
23
+
24
+ <script setup lang="ts">
25
+ import Aerogel from 'virtual:aerogel';
26
+
27
+ import { computed } from 'vue';
28
+
29
+ import Markdown from '@aerogel/core/components/ui/Markdown.vue';
30
+ import Button from '@aerogel/core/components/ui/Button.vue';
31
+ import Select from '@aerogel/core/components/ui/Select.vue';
32
+ import SelectLabel from '@aerogel/core/components/ui/SelectLabel.vue';
33
+ import SelectTrigger from '@aerogel/core/components/ui/SelectTrigger.vue';
34
+ import SelectOptions from '@aerogel/core/components/ui/SelectOptions.vue';
35
+ import { Lang, translateWithDefault } from '@aerogel/core/lang';
36
+
37
+ const browserLocale = Lang.getBrowserLocale();
38
+ const options = computed(() => [null, ...Lang.locales]);
39
+
40
+ function renderLocale(locale: string | null): string {
41
+ return (
42
+ (locale && Aerogel.locales[locale]) ??
43
+ translateWithDefault('settings.localeDefault', '{locale} (default)', {
44
+ locale: Aerogel.locales[browserLocale] ?? browserLocale,
45
+ })
46
+ );
47
+ }
48
+ </script>
@@ -0,0 +1,10 @@
1
+ import { defineSettings } from '@aerogel/core/services';
2
+
3
+ import Language from './Language.vue';
4
+
5
+ export default defineSettings([
6
+ {
7
+ priority: 100,
8
+ component: Language,
9
+ },
10
+ ]);
@@ -1,6 +1,6 @@
1
1
  import type { App } from 'vue';
2
2
 
3
- import type { AerogelOptions } from '@/bootstrap/options';
3
+ import type { AerogelOptions } from '@aerogel/core/bootstrap/options';
4
4
 
5
5
  export interface Plugin {
6
6
  name?: string;
@@ -1,6 +1,6 @@
1
1
  import type { GetClosureArgs } from '@noeldemartin/utils';
2
2
 
3
- import App from '@/services/App';
3
+ import App from '@aerogel/core/services/App';
4
4
 
5
5
  import type { Plugin } from './Plugin';
6
6
 
@@ -13,13 +13,16 @@ export function definePlugin<T extends Plugin>(plugin: T): T {
13
13
  export async function installPlugins(plugins: Plugin[], ...args: GetClosureArgs<Plugin['install']>): Promise<void> {
14
14
  App.setState(
15
15
  'plugins',
16
- plugins.reduce((pluginsMap, plugin) => {
17
- if (plugin.name) {
18
- pluginsMap[plugin.name] = plugin;
19
- }
16
+ plugins.reduce(
17
+ (pluginsMap, plugin) => {
18
+ if (plugin.name) {
19
+ pluginsMap[plugin.name] = plugin;
20
+ }
20
21
 
21
- return pluginsMap;
22
- }, {} as Record<string, Plugin>),
22
+ return pluginsMap;
23
+ },
24
+ {} as Record<string, Plugin>,
25
+ ),
23
26
  );
24
27
 
25
28
  await Promise.all(plugins.map((plugin) => plugin.install(...args)) ?? []);
@@ -1,18 +1,29 @@
1
1
  import Aerogel from 'virtual:aerogel';
2
2
 
3
- import type { App } from 'vue';
3
+ import { getEnv } from '@noeldemartin/utils';
4
+ import type { App, Component } from 'vue';
4
5
 
5
- import { defineServiceState } from '@/services/Service';
6
- import type { Plugin } from '@/plugins/Plugin';
6
+ import { defineServiceState } from '@aerogel/core/services/Service';
7
+ import type { Plugin } from '@aerogel/core/plugins/Plugin';
8
+
9
+ export interface AppSetting {
10
+ component: Component;
11
+ priority: number;
12
+ }
13
+
14
+ export function defineSettings<T extends AppSetting[]>(settings: T): T {
15
+ return settings;
16
+ }
7
17
 
8
18
  export default defineServiceState({
9
19
  name: 'app',
10
20
  initialState: {
11
21
  plugins: {} as Record<string, Plugin>,
12
22
  instance: null as App | null,
13
- environment: Aerogel.environment,
23
+ environment: getEnv() ?? 'development',
14
24
  version: Aerogel.version,
15
25
  sourceUrl: Aerogel.sourceUrl,
26
+ settings: [] as AppSetting[],
16
27
  },
17
28
  computed: {
18
29
  development: (state) => state.environment === 'development',
@@ -1,13 +1,17 @@
1
1
  import Aerogel from 'virtual:aerogel';
2
2
 
3
3
  import { PromisedValue, facade, forever, updateLocationQueryParameters } from '@noeldemartin/utils';
4
+ import { markRaw } from 'vue';
4
5
 
5
- import Events from '@/services/Events';
6
- import type { Plugin } from '@/plugins';
7
- import type { Services } from '@/services';
6
+ import Events from '@aerogel/core/services/Events';
7
+ import type { Plugin } from '@aerogel/core/plugins';
8
+ import type { AppSetting, Services } from '@aerogel/core/services';
8
9
 
9
10
  import Service from './App.state';
10
11
 
12
+ export { defineSettings } from './App.state';
13
+ export type { AppSetting } from './App.state';
14
+
11
15
  export class AppService extends Service {
12
16
 
13
17
  public readonly name = Aerogel.name;
@@ -22,6 +26,10 @@ export class AppService extends Service {
22
26
  return this.mounted.isResolved();
23
27
  }
24
28
 
29
+ public addSetting(setting: AppSetting): void {
30
+ this.settings.push(markRaw(setting));
31
+ }
32
+
25
33
  public async whenReady<T>(callback: () => T): Promise<T> {
26
34
  const result = await this.ready.then(callback);
27
35
 
@@ -45,7 +53,7 @@ export class AppService extends Service {
45
53
  return this.instance?.config.globalProperties[name] ?? null;
46
54
  }
47
55
 
48
- protected async boot(): Promise<void> {
56
+ protected override async boot(): Promise<void> {
49
57
  Events.once('application-ready', () => this.ready.resolve());
50
58
  Events.once('application-mounted', () => this.mounted.resolve());
51
59
  }
@@ -1,6 +1,6 @@
1
1
  import { PromisedValue, facade, tap } from '@noeldemartin/utils';
2
2
 
3
- import Service from '@/services/Service';
3
+ import Service from '@aerogel/core/services/Service';
4
4
 
5
5
  export class CacheService extends Service {
6
6
 
@@ -10,12 +10,12 @@ describe('Events', () => {
10
10
  // Arrange
11
11
  let counter = 0;
12
12
 
13
- Events.on('trigger', () => counter++);
13
+ Events.on('application-mounted', () => counter++);
14
14
 
15
15
  // Act
16
- await Events.emit('trigger');
17
- await Events.emit('trigger');
18
- await Events.emit('trigger');
16
+ await Events.emit('application-mounted');
17
+ await Events.emit('application-mounted');
18
+ await Events.emit('application-mounted');
19
19
 
20
20
  // Assert
21
21
  expect(counter).toEqual(3);
@@ -25,12 +25,12 @@ describe('Events', () => {
25
25
  // Arrange
26
26
  const storage: string[] = [];
27
27
 
28
- Events.on('trigger', () => storage.push('second'));
29
- Events.on('trigger', { priority: EventListenerPriorities.Low }, () => storage.push('third'));
30
- Events.on('trigger', { priority: EventListenerPriorities.High }, () => storage.push('first'));
28
+ Events.on('application-mounted', () => storage.push('second'));
29
+ Events.on('application-mounted', { priority: EventListenerPriorities.Low }, () => storage.push('third'));
30
+ Events.on('application-mounted', { priority: EventListenerPriorities.High }, () => storage.push('first'));
31
31
 
32
32
  // Act
33
- await Events.emit('trigger');
33
+ await Events.emit('application-mounted');
34
34
 
35
35
  // Assert
36
36
  expect(storage).toEqual(['first', 'second', 'third']);
@@ -1,6 +1,6 @@
1
1
  import { arrayRemove, facade, fail, tap } from '@noeldemartin/utils';
2
2
 
3
- import Service from '@/services/Service';
3
+ import Service from '@aerogel/core/services/Service';
4
4
 
5
5
  export interface EventsPayload {}
6
6
  export interface EventListenerOptions {
@@ -10,7 +10,6 @@ export type AerogelGlobalEvents = Partial<{ [Event in EventWithoutPayload]: () =
10
10
  Partial<{ [Event in EventWithPayload]: EventListener<EventsPayload[Event]> }>;
11
11
 
12
12
  export type EventListener<T = unknown> = (payload: T) => unknown;
13
- export type UnknownEvent<T> = T extends keyof EventsPayload ? never : T;
14
13
 
15
14
  export type EventWithoutPayload = {
16
15
  [K in keyof EventsPayload]: EventsPayload[K] extends void ? K : never;
@@ -32,14 +31,14 @@ export class EventsService extends Service {
32
31
 
33
32
  private listeners: Record<string, { priorities: number[]; handlers: Record<number, EventListener[]> }> = {};
34
33
 
35
- protected async boot(): Promise<void> {
34
+ protected override async boot(): Promise<void> {
36
35
  Object.entries(globalThis.__aerogelEvents__ ?? {}).forEach(([event, listener]) =>
37
- this.on(event as string, listener as EventListener));
36
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
37
+ this.on(event as any, listener as EventListener));
38
38
  }
39
39
 
40
40
  public emit<Event extends EventWithoutPayload>(event: Event): Promise<void>;
41
41
  public emit<Event extends EventWithPayload>(event: Event, payload: EventsPayload[Event]): Promise<void>;
42
- public emit<Event extends string>(event: UnknownEvent<Event>, payload?: unknown): Promise<void>;
43
42
  public async emit(event: string, payload?: unknown): Promise<void> {
44
43
  const listeners = this.listeners[event] ?? { priorities: [], handlers: {} };
45
44
 
@@ -55,9 +54,6 @@ export class EventsService extends Service {
55
54
  public on<Event extends EventWithPayload>(event: Event, listener: EventListener<EventsPayload[Event]>): () => void | void; // prettier-ignore
56
55
  public on<Event extends EventWithPayload>(event: Event, priority: EventListenerPriority, listener: EventListener<EventsPayload[Event]>): () => void | void; // prettier-ignore
57
56
  public on<Event extends EventWithPayload>(event: Event, options: Partial<EventListenerOptions>, listener: EventListener<EventsPayload[Event]>): () => void | void; // prettier-ignore
58
- public on<Event extends string>(event: UnknownEvent<Event>, listener: EventListener): () => void;
59
- public on<Event extends string>(event: UnknownEvent<Event>, priority: EventListenerPriority, listener: EventListener): () => void; // prettier-ignore
60
- public on<Event extends string>(event: UnknownEvent<Event>, options: Partial<EventListenerOptions>, listener: EventListener): () => void; // prettier-ignore
61
57
  /* eslint-enable max-len */
62
58
 
63
59
  public on(
@@ -83,8 +79,6 @@ export class EventsService extends Service {
83
79
  public once<Event extends EventWithoutPayload>(event: Event, options: Partial<EventListenerOptions>, listener: () => unknown): () => void; // prettier-ignore
84
80
  public once<Event extends EventWithPayload>(event: Event, listener: EventListener<EventsPayload[Event]>): () => void | void; // prettier-ignore
85
81
  public once<Event extends EventWithPayload>(event: Event, options: Partial<EventListenerOptions>, listener: EventListener<EventsPayload[Event]>): () => void | void; // prettier-ignore
86
- public once<Event extends string>(event: UnknownEvent<Event>, listener: EventListener): () => void;
87
- public once<Event extends string>(event: UnknownEvent<Event>, options: Partial<EventListenerOptions>, listener: EventListener): () => void; // prettier-ignore
88
82
  /* eslint-enable max-len */
89
83
 
90
84
  public once(
@@ -11,9 +11,9 @@ import {
11
11
  import type { Constructor, Nullable } from '@noeldemartin/utils';
12
12
  import type { Store } from 'pinia';
13
13
 
14
- import ServiceBootError from '@/errors/ServiceBootError';
15
- import { defineServiceStore } from '@/services/store';
16
- import type { Unref } from '@/utils/vue';
14
+ import ServiceBootError from '@aerogel/core/errors/ServiceBootError';
15
+ import { defineServiceStore } from '@aerogel/core/services/store';
16
+ import type { Unref } from '@aerogel/core/utils/vue';
17
17
 
18
18
  export type ServiceState = Record<string, any>; // eslint-disable-line @typescript-eslint/no-explicit-any
19
19
  export type DefaultServiceState = any; // eslint-disable-line @typescript-eslint/no-explicit-any
@@ -32,7 +32,7 @@ export type StateWatchers<TService extends Service, TState extends ServiceState>
32
32
  export type ServiceWithState<
33
33
  State extends ServiceState = ServiceState,
34
34
  ComputedState extends ServiceState = {},
35
- ServiceStorage = Partial<State>
35
+ ServiceStorage = Partial<State>,
36
36
  > = Constructor<Unref<State>> &
37
37
  Constructor<ComputedState> &
38
38
  Constructor<Service<Unref<State>, ComputedState, Unref<ServiceStorage>>>;
@@ -40,7 +40,7 @@ export type ServiceWithState<
40
40
  export function defineServiceState<
41
41
  State extends ServiceState = ServiceState,
42
42
  ComputedState extends ServiceState = {},
43
- ServiceStorage = Partial<State>
43
+ ServiceStorage = Partial<State>,
44
44
  >(options: {
45
45
  name: string;
46
46
  initialState: State | (() => State);
@@ -52,17 +52,17 @@ export function defineServiceState<
52
52
  }): ServiceWithState<State, ComputedState, ServiceStorage> {
53
53
  return class extends Service<Unref<State>, ComputedState, ServiceStorage> {
54
54
 
55
- public static persist = (options.persist as string[]) ?? [];
55
+ public static override persist = (options.persist as string[]) ?? [];
56
56
 
57
- protected usesStore(): boolean {
57
+ protected override usesStore(): boolean {
58
58
  return true;
59
59
  }
60
60
 
61
- protected getName(): string | null {
61
+ protected override getName(): string | null {
62
62
  return options.name ?? null;
63
63
  }
64
64
 
65
- protected getInitialState(): Unref<State> {
65
+ protected override getInitialState(): Unref<State> {
66
66
  if (typeof options.initialState === 'function') {
67
67
  return options.initialState();
68
68
  }
@@ -86,19 +86,19 @@ export function defineServiceState<
86
86
  }, {} as Unref<State>);
87
87
  }
88
88
 
89
- protected getComputedStateDefinition(): ComputedStateDefinition<Unref<State>, ComputedState> {
89
+ protected override getComputedStateDefinition(): ComputedStateDefinition<Unref<State>, ComputedState> {
90
90
  return (options.computed ?? {}) as ComputedStateDefinition<Unref<State>, ComputedState>;
91
91
  }
92
92
 
93
- protected getStateWatchers(): StateWatchers<Service, Unref<State>> {
93
+ protected override getStateWatchers(): StateWatchers<Service, Unref<State>> {
94
94
  return (options.watch ?? {}) as StateWatchers<Service, Unref<State>>;
95
95
  }
96
96
 
97
- protected serializePersistedState(state: Partial<State>): ServiceStorage {
97
+ protected override serializePersistedState(state: Partial<State>): ServiceStorage {
98
98
  return options.serialize?.(state) ?? (state as ServiceStorage);
99
99
  }
100
100
 
101
- protected deserializePersistedState(state: ServiceStorage): Partial<State> {
101
+ protected override deserializePersistedState(state: ServiceStorage): Partial<State> {
102
102
  return options.restore?.(state) ?? (state as Partial<State>);
103
103
  }
104
104
 
@@ -108,7 +108,7 @@ export function defineServiceState<
108
108
  export default class Service<
109
109
  State extends ServiceState = DefaultServiceState,
110
110
  ComputedState extends ServiceState = {},
111
- ServiceStorage = Partial<State>
111
+ ServiceStorage = Partial<State>,
112
112
  > extends MagicObject {
113
113
 
114
114
  public static persist: string[] = [];
@@ -142,9 +142,9 @@ export default class Service<
142
142
  return this._booted;
143
143
  }
144
144
 
145
- public static<T extends typeof Service>(): T;
146
- public static<T extends typeof Service, K extends keyof T>(property: K): T[K];
147
- public static<T extends typeof Service, K extends keyof T>(property?: K): T | T[K] {
145
+ public override static<T extends typeof Service>(): T;
146
+ public override static<T extends typeof Service, K extends keyof T>(property: K): T[K];
147
+ public override static<T extends typeof Service, K extends keyof T>(property?: K): T | T[K] {
148
148
  return super.static<T, K>(property as K);
149
149
  }
150
150
 
@@ -182,10 +182,10 @@ export default class Service<
182
182
  const store = this._store as any;
183
183
 
184
184
  if (property) {
185
- return store ? store[property] : undefined;
185
+ return store ? store[property] : (undefined as State[P]);
186
186
  }
187
187
 
188
- return store ? store : {};
188
+ return store ? store : ({} as State);
189
189
  }
190
190
 
191
191
  public setState<P extends keyof State>(property: P, value: State[P]): void;
@@ -219,7 +219,7 @@ export default class Service<
219
219
  this.onPersistentStateUpdated(state);
220
220
  }
221
221
 
222
- protected __get(property: string): unknown {
222
+ protected override __get(property: string): unknown {
223
223
  if (this.hasState(property)) {
224
224
  return this.getState(property);
225
225
  }
@@ -227,7 +227,7 @@ export default class Service<
227
227
  return super.__get(property);
228
228
  }
229
229
 
230
- protected __set(property: string, value: unknown): void {
230
+ protected override __set(property: string, value: unknown): void {
231
231
  this.setState({ [property]: value } as Partial<State>);
232
232
  }
233
233
 
@@ -1,7 +1,7 @@
1
1
  import { facade } from '@noeldemartin/utils';
2
2
 
3
- import Events from '@/services/Events';
4
- import Service from '@/services/Service';
3
+ import Events from '@aerogel/core/services/Events';
4
+ import Service from '@aerogel/core/services/Service';
5
5
 
6
6
  export class StorageService extends Service {
7
7
 
@@ -13,7 +13,7 @@ export class StorageService extends Service {
13
13
 
14
14
  export default facade(StorageService);
15
15
 
16
- declare module '@/services/Events' {
16
+ declare module '@aerogel/core/services/Events' {
17
17
  export interface EventsPayload {
18
18
  'purge-storage': void;
19
19
  }
@@ -1,6 +1,7 @@
1
1
  import type { App as VueApp } from 'vue';
2
2
 
3
- import { definePlugin } from '@/plugins';
3
+ import { definePlugin } from '@aerogel/core/plugins';
4
+ import { isDevelopment, isTesting } from '@noeldemartin/utils';
4
5
 
5
6
  import App from './App';
6
7
  import Cache from './Cache';
@@ -8,6 +9,7 @@ import Events from './Events';
8
9
  import Service from './Service';
9
10
  import Storage from './Storage';
10
11
  import { getPiniaStore } from './store';
12
+ import type { AppSetting } from './App.state';
11
13
 
12
14
  export * from './App';
13
15
  export * from './Cache';
@@ -39,7 +41,7 @@ export async function bootServices(app: VueApp, services: Record<string, Service
39
41
 
40
42
  Object.assign(app.config.globalProperties, services);
41
43
 
42
- if (App.development || App.testing) {
44
+ if (isDevelopment() || isTesting()) {
43
45
  Object.assign(globalThis, services);
44
46
  }
45
47
  }
@@ -52,17 +54,19 @@ export default definePlugin({
52
54
  };
53
55
 
54
56
  app.use(getPiniaStore());
57
+ options.settings?.forEach((setting) => App.addSetting(setting));
55
58
 
56
59
  await bootServices(app, services);
57
60
  },
58
61
  });
59
62
 
60
- declare module '@/bootstrap/options' {
63
+ declare module '@aerogel/core/bootstrap/options' {
61
64
  export interface AerogelOptions {
62
65
  services?: Record<string, Service>;
66
+ settings?: AppSetting[];
63
67
  }
64
68
  }
65
69
 
66
- declare module '@vue/runtime-core' {
70
+ declare module 'vue' {
67
71
  interface ComponentCustomProperties extends Services {}
68
72
  }
@@ -2,14 +2,14 @@ import { objectOnly } from '@noeldemartin/utils';
2
2
 
3
3
  export type Replace<
4
4
  TOriginal extends Record<string, unknown>,
5
- TReplacements extends Partial<Record<keyof TOriginal, unknown>>
5
+ TReplacements extends Partial<Record<keyof TOriginal, unknown>>,
6
6
  > = {
7
7
  [K in keyof TOriginal]: TReplacements extends Record<K, infer Replacement> ? Replacement : TOriginal[K];
8
8
  };
9
9
 
10
10
  export function replaceExisting<
11
11
  TOriginal extends Record<string, unknown>,
12
- TReplacements extends Partial<Record<keyof TOriginal, unknown>>
12
+ TReplacements extends Partial<Record<keyof TOriginal, unknown>>,
13
13
  >(original: TOriginal, replacements: TReplacements): Replace<TOriginal, TReplacements> {
14
14
  return {
15
15
  ...original,
@@ -1,7 +1,8 @@
1
+ import { isTesting } from '@noeldemartin/utils';
1
2
  import type { GetClosureArgs } from '@noeldemartin/utils';
2
3
 
3
- import Events from '@/services/Events';
4
- import { definePlugin } from '@/plugins';
4
+ import Events from '@aerogel/core/services/Events';
5
+ import { definePlugin } from '@aerogel/core/plugins';
5
6
 
6
7
  export interface AerogelTestingRuntime {
7
8
  on: (typeof Events)['on'];
@@ -9,7 +10,7 @@ export interface AerogelTestingRuntime {
9
10
 
10
11
  export default definePlugin({
11
12
  async install() {
12
- if (import.meta.env.MODE !== 'testing') {
13
+ if (!isTesting()) {
13
14
  return;
14
15
  }
15
16
 
@@ -1,27 +1,11 @@
1
- import { mock, tap, toString } from '@noeldemartin/utils';
1
+ import { FakeLocalStorage } from '@noeldemartin/testing';
2
2
  import { beforeEach, vi } from 'vitest';
3
3
 
4
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
5
- tap(globalThis, (global: any) => {
6
- const localStorage: Record<string, string> = {};
7
-
8
- global.jest = vi;
9
- global.navigator = { languages: ['en'] };
10
- global.localStorage = mock<Storage>({
11
- getItem: (key) => localStorage[key] ?? null,
12
- setItem(key, value) {
13
- localStorage[key] = toString(value);
14
- },
15
- });
16
- });
17
-
18
4
  vi.mock('dompurify', async () => {
19
5
  return { default: { sanitize: (html: string) => html } };
20
6
  });
21
7
 
22
8
  beforeEach(() => {
23
- vi.stubGlobal('document', {
24
- querySelector: () => null,
25
- getElementById: () => null,
26
- });
9
+ FakeLocalStorage.reset();
10
+ FakeLocalStorage.patchGlobal();
27
11
  });