@aerogel/core 0.0.0-next.59bf5f7cc06e728d0cf6c00de28f1da48d7d6b8e → 0.0.0-next.926bde19326fe7b6b24b277666936862b64d8295
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/dist/aerogel-core.cjs.js +1 -1
- package/dist/aerogel-core.d.ts +39 -201
- package/dist/aerogel-core.esm.js +1 -1
- package/package.json +1 -2
- package/src/bootstrap/index.ts +1 -4
- package/src/components/basic/AGMarkdown.vue +3 -3
- package/src/components/forms/AGButton.vue +8 -21
- package/src/components/forms/AGInput.vue +4 -8
- package/src/components/forms/index.ts +1 -2
- package/src/components/headless/forms/AGHeadlessInput.ts +2 -2
- package/src/components/headless/forms/AGHeadlessInput.vue +3 -3
- package/src/components/headless/forms/AGHeadlessInputError.vue +1 -1
- package/src/components/headless/forms/AGHeadlessInputInput.vue +3 -15
- package/src/components/headless/modals/AGHeadlessModalPanel.vue +1 -5
- package/src/components/modals/AGModal.vue +2 -20
- package/src/components/modals/index.ts +1 -2
- package/src/forms/Form.ts +9 -12
- package/src/forms/utils.ts +0 -17
- package/src/lang/Lang.ts +3 -11
- package/src/lang/index.ts +4 -2
- package/src/main.ts +0 -1
- package/src/services/App.state.ts +0 -3
- package/src/services/App.ts +1 -11
- package/src/services/Service.ts +44 -126
- package/src/services/index.ts +4 -18
- package/src/ui/UI.state.ts +0 -1
- package/src/ui/UI.ts +0 -15
- package/src/ui/index.ts +0 -2
- package/src/utils/index.ts +0 -1
- package/src/components/forms/AGCheckbox.vue +0 -35
- package/src/components/modals/AGLoadingModal.vue +0 -19
- package/src/errors/Errors.state.ts +0 -31
- package/src/errors/Errors.ts +0 -132
- package/src/errors/index.ts +0 -21
- package/src/lang/utils.ts +0 -4
- package/src/services/store.ts +0 -27
- package/src/utils/composition/forms.ts +0 -11
package/src/services/Service.ts
CHANGED
|
@@ -1,42 +1,27 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { computed, reactive } from 'vue';
|
|
2
|
+
import { MagicObject, PromisedValue } from '@noeldemartin/utils';
|
|
3
|
+
import type { ComputedRef } from 'vue';
|
|
2
4
|
import type { Constructor } from '@noeldemartin/utils';
|
|
3
|
-
import type { Store } from 'pinia';
|
|
4
5
|
|
|
5
6
|
import ServiceBootError from '@/errors/ServiceBootError';
|
|
6
|
-
import { defineServiceStore } from '@/services/store';
|
|
7
7
|
|
|
8
8
|
export type ServiceState = Record<string, any>; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
9
|
-
export type DefaultServiceState =
|
|
9
|
+
export type DefaultServiceState = {};
|
|
10
10
|
export type ServiceConstructor<T extends Service = Service> = Constructor<T> & typeof Service;
|
|
11
11
|
|
|
12
12
|
export type ComputedStateDefinition<TState extends ServiceState, TComputedState extends ServiceState> = {
|
|
13
13
|
[K in keyof TComputedState]: (state: TState) => TComputedState[K];
|
|
14
|
-
}
|
|
15
|
-
readonly [K in keyof TComputedState]: TComputedState[K];
|
|
16
|
-
}>;
|
|
14
|
+
};
|
|
17
15
|
|
|
18
16
|
export function defineServiceState<
|
|
19
17
|
State extends ServiceState = ServiceState,
|
|
20
18
|
ComputedState extends ServiceState = {}
|
|
21
19
|
>(options: {
|
|
22
|
-
name: string;
|
|
23
20
|
initialState: State;
|
|
24
|
-
persist?: (keyof State)[];
|
|
25
21
|
computed?: ComputedStateDefinition<State, ComputedState>;
|
|
26
|
-
|
|
27
|
-
}): Constructor<State> & Constructor<ComputedState> & Constructor<Service<State, ComputedState, Partial<State>>> {
|
|
22
|
+
}): Constructor<State> & Constructor<ComputedState> & ServiceConstructor {
|
|
28
23
|
return class extends Service<State, ComputedState> {
|
|
29
24
|
|
|
30
|
-
public static persist = (options.persist as string[]) ?? [];
|
|
31
|
-
|
|
32
|
-
protected usesStore(): boolean {
|
|
33
|
-
return true;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
protected getName(): string | null {
|
|
37
|
-
return options.name ?? null;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
25
|
protected getInitialState(): State {
|
|
41
26
|
return options.initialState;
|
|
42
27
|
}
|
|
@@ -44,53 +29,44 @@ export function defineServiceState<
|
|
|
44
29
|
protected getComputedStateDefinition(): ComputedStateDefinition<State, ComputedState> {
|
|
45
30
|
return options.computed ?? ({} as ComputedStateDefinition<State, ComputedState>);
|
|
46
31
|
}
|
|
47
|
-
|
|
48
|
-
protected serializePersistedState(state: Partial<State>): Partial<State> {
|
|
49
|
-
return options.serialize?.(state) ?? state;
|
|
50
|
-
}
|
|
51
32
|
|
|
52
|
-
} as unknown as Constructor<State> &
|
|
53
|
-
Constructor<ComputedState> &
|
|
54
|
-
Constructor<Service<State, ComputedState, Partial<State>>>;
|
|
33
|
+
} as unknown as Constructor<State> & Constructor<ComputedState> & ServiceConstructor;
|
|
55
34
|
}
|
|
56
35
|
|
|
57
36
|
export default class Service<
|
|
58
37
|
State extends ServiceState = DefaultServiceState,
|
|
59
|
-
ComputedState extends ServiceState = {}
|
|
60
|
-
ServiceStorage extends Partial<State> = Partial<State>
|
|
38
|
+
ComputedState extends ServiceState = {}
|
|
61
39
|
> extends MagicObject {
|
|
62
40
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
protected _name: string;
|
|
41
|
+
protected _namespace: string;
|
|
66
42
|
private _booted: PromisedValue<void>;
|
|
67
|
-
private
|
|
68
|
-
private
|
|
43
|
+
private _state: State;
|
|
44
|
+
private _computedState: Record<keyof ComputedState, ComputedRef>;
|
|
69
45
|
|
|
70
46
|
constructor() {
|
|
71
47
|
super();
|
|
72
48
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
this._name = this.getName() ?? new.target.name;
|
|
49
|
+
this._namespace = new.target.name;
|
|
76
50
|
this._booted = new PromisedValue();
|
|
77
|
-
this.
|
|
78
|
-
this.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
51
|
+
this._state = reactive(this.getInitialState());
|
|
52
|
+
this._computedState = Object.entries(this.getComputedStateDefinition()).reduce(
|
|
53
|
+
(computedState, [name, method]) => {
|
|
54
|
+
computedState[name as keyof ComputedState] = computed(() => method(this._state));
|
|
55
|
+
|
|
56
|
+
return computedState;
|
|
57
|
+
},
|
|
58
|
+
{} as Record<keyof ComputedState, ComputedRef>,
|
|
59
|
+
);
|
|
86
60
|
}
|
|
87
61
|
|
|
88
62
|
public get booted(): PromisedValue<void> {
|
|
89
63
|
return this._booted;
|
|
90
64
|
}
|
|
91
65
|
|
|
92
|
-
public launch(): Promise<void> {
|
|
93
|
-
const handleError = (error: unknown) => this._booted.reject(new ServiceBootError(this.
|
|
66
|
+
public launch(namespace?: string): Promise<void> {
|
|
67
|
+
const handleError = (error: unknown) => this._booted.reject(new ServiceBootError(this._namespace, error));
|
|
68
|
+
|
|
69
|
+
this._namespace = namespace ?? this._namespace;
|
|
94
70
|
|
|
95
71
|
try {
|
|
96
72
|
this.boot()
|
|
@@ -103,48 +79,15 @@ export default class Service<
|
|
|
103
79
|
return this._booted;
|
|
104
80
|
}
|
|
105
81
|
|
|
106
|
-
public hasState<P extends keyof State>(property: P): boolean {
|
|
107
|
-
if (!this._store) {
|
|
108
|
-
return false;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return property in this._store.$state || this._computedStateKeys.has(property);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
public getState(): State;
|
|
115
|
-
public getState<P extends keyof State>(property: P): State[P];
|
|
116
|
-
public getState<P extends keyof State>(property?: P): State | State[P] {
|
|
117
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
118
|
-
const store = this._store as any;
|
|
119
|
-
|
|
120
|
-
if (property) {
|
|
121
|
-
return store ? store[property] : undefined;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return store ? store : {};
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
public setState<P extends keyof State>(property: P, value: State[P]): void;
|
|
128
|
-
public setState(state: Partial<State>): void;
|
|
129
|
-
public setState<P extends keyof State>(stateOrProperty: P | Partial<State>, value?: State[P]): void {
|
|
130
|
-
if (!this._store) {
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const state = (
|
|
135
|
-
typeof stateOrProperty === 'string' ? { [stateOrProperty]: value } : stateOrProperty
|
|
136
|
-
) as Partial<State>;
|
|
137
|
-
|
|
138
|
-
Object.assign(this._store.$state, state);
|
|
139
|
-
|
|
140
|
-
this.onStateUpdated(state);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
82
|
protected __get(property: string): unknown {
|
|
144
83
|
if (this.hasState(property)) {
|
|
145
84
|
return this.getState(property);
|
|
146
85
|
}
|
|
147
86
|
|
|
87
|
+
if (this.hasComputedState(property)) {
|
|
88
|
+
return this.getComputedState(property);
|
|
89
|
+
}
|
|
90
|
+
|
|
148
91
|
return super.__get(property);
|
|
149
92
|
}
|
|
150
93
|
|
|
@@ -152,29 +95,26 @@ export default class Service<
|
|
|
152
95
|
this.setState({ [property]: value } as Partial<State>);
|
|
153
96
|
}
|
|
154
97
|
|
|
155
|
-
protected
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
const persisted = objectOnly(state, persist);
|
|
159
|
-
|
|
160
|
-
if (isEmpty(persisted)) {
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
98
|
+
protected hasState<P extends keyof State>(property: P): boolean {
|
|
99
|
+
return property in this._state;
|
|
100
|
+
}
|
|
163
101
|
|
|
164
|
-
|
|
102
|
+
protected hasComputedState<P extends keyof State>(property: P): boolean {
|
|
103
|
+
return property in this._computedState;
|
|
104
|
+
}
|
|
165
105
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
106
|
+
protected getState(): State;
|
|
107
|
+
protected getState<P extends keyof State>(property: P): State[P];
|
|
108
|
+
protected getState<P extends keyof State>(property?: P): State | State[P] {
|
|
109
|
+
return property ? this._state[property] : this._state;
|
|
170
110
|
}
|
|
171
111
|
|
|
172
|
-
protected
|
|
173
|
-
return
|
|
112
|
+
protected getComputedState<P extends keyof ComputedState>(property: P): ComputedState[P] {
|
|
113
|
+
return this._computedState[property]?.value;
|
|
174
114
|
}
|
|
175
115
|
|
|
176
|
-
protected
|
|
177
|
-
|
|
116
|
+
protected setState(state: Partial<State>): void {
|
|
117
|
+
Object.assign(this._state, state);
|
|
178
118
|
}
|
|
179
119
|
|
|
180
120
|
protected getInitialState(): State {
|
|
@@ -185,30 +125,8 @@ export default class Service<
|
|
|
185
125
|
return {} as ComputedStateDefinition<State, ComputedState>;
|
|
186
126
|
}
|
|
187
127
|
|
|
188
|
-
protected serializePersistedState(state: Partial<State>): Partial<State> {
|
|
189
|
-
return state;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
128
|
protected async boot(): Promise<void> {
|
|
193
|
-
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
protected restorePersistedState(): void {
|
|
197
|
-
// TODO fix this.static()
|
|
198
|
-
const persist = (this.constructor as unknown as { persist: string[] }).persist;
|
|
199
|
-
|
|
200
|
-
if (!this.usesStore() || isEmpty(persist)) {
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
if (Storage.has(this._name)) {
|
|
205
|
-
const persisted = Storage.require<ServiceStorage>(this._name);
|
|
206
|
-
this.setState(persisted);
|
|
207
|
-
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
Storage.set(this._name, objectOnly(this.getState(), persist));
|
|
129
|
+
//
|
|
212
130
|
}
|
|
213
131
|
|
|
214
132
|
}
|
package/src/services/index.ts
CHANGED
|
@@ -5,7 +5,6 @@ import { definePlugin } from '@/plugins';
|
|
|
5
5
|
import App from './App';
|
|
6
6
|
import Events from './Events';
|
|
7
7
|
import Service from './Service';
|
|
8
|
-
import { getPiniaStore } from './store';
|
|
9
8
|
|
|
10
9
|
export * from './App';
|
|
11
10
|
export * from './Events';
|
|
@@ -24,9 +23,9 @@ export interface Services extends DefaultServices {}
|
|
|
24
23
|
|
|
25
24
|
export async function bootServices(app: VueApp, services: Record<string, Service>): Promise<void> {
|
|
26
25
|
await Promise.all(
|
|
27
|
-
Object.entries(services).map(async ([
|
|
26
|
+
Object.entries(services).map(async ([name, service]) => {
|
|
28
27
|
// eslint-disable-next-line no-console
|
|
29
|
-
await service.launch().catch((error) => console.error(error));
|
|
28
|
+
await service.launch(name.slice(1)).catch((error) => console.error(error));
|
|
30
29
|
}),
|
|
31
30
|
);
|
|
32
31
|
|
|
@@ -34,24 +33,11 @@ export async function bootServices(app: VueApp, services: Record<string, Service
|
|
|
34
33
|
}
|
|
35
34
|
|
|
36
35
|
export default definePlugin({
|
|
37
|
-
async install(app
|
|
38
|
-
|
|
39
|
-
...defaultServices,
|
|
40
|
-
...options.services,
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
app.use(getPiniaStore());
|
|
44
|
-
|
|
45
|
-
await bootServices(app, services);
|
|
36
|
+
async install(app) {
|
|
37
|
+
await bootServices(app, defaultServices);
|
|
46
38
|
},
|
|
47
39
|
});
|
|
48
40
|
|
|
49
|
-
declare module '@/bootstrap/options' {
|
|
50
|
-
interface AerogelOptions {
|
|
51
|
-
services?: Record<string, Service>;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
41
|
declare module '@vue/runtime-core' {
|
|
56
42
|
interface ComponentCustomProperties extends Services {}
|
|
57
43
|
}
|
package/src/ui/UI.state.ts
CHANGED
package/src/ui/UI.ts
CHANGED
|
@@ -21,7 +21,6 @@ type ModalResult<TComponent> = TComponent extends ModalComponent<Record<string,
|
|
|
21
21
|
export const UIComponents = {
|
|
22
22
|
AlertModal: 'alert-modal',
|
|
23
23
|
ConfirmModal: 'confirm-modal',
|
|
24
|
-
LoadingModal: 'loading-modal',
|
|
25
24
|
} as const;
|
|
26
25
|
|
|
27
26
|
export type UIComponent = ObjectValues<typeof UIComponents>;
|
|
@@ -52,20 +51,6 @@ export class UIService extends Service {
|
|
|
52
51
|
return result ?? false;
|
|
53
52
|
}
|
|
54
53
|
|
|
55
|
-
public async loading<T>(operation: Promise<T>): Promise<T>;
|
|
56
|
-
public async loading<T>(message: string, operation: Promise<T>): Promise<T>;
|
|
57
|
-
public async loading<T>(messageOrOperation: string | Promise<T>, operation?: Promise<T>): Promise<T> {
|
|
58
|
-
operation = typeof messageOrOperation === 'string' ? (operation as Promise<T>) : messageOrOperation;
|
|
59
|
-
|
|
60
|
-
const message = typeof messageOrOperation === 'string' ? messageOrOperation : undefined;
|
|
61
|
-
const modal = await this.openModal(this.requireComponent(UIComponents.LoadingModal), { message });
|
|
62
|
-
const result = await operation;
|
|
63
|
-
|
|
64
|
-
await this.closeModal(modal.id);
|
|
65
|
-
|
|
66
|
-
return result;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
54
|
public registerComponent(name: UIComponent, component: Component): void {
|
|
70
55
|
this.components[name] = component;
|
|
71
56
|
}
|
package/src/ui/index.ts
CHANGED
|
@@ -6,7 +6,6 @@ import { definePlugin } from '@/plugins';
|
|
|
6
6
|
import UI, { UIComponents } from './UI';
|
|
7
7
|
import AGAlertModal from '../components/modals/AGAlertModal.vue';
|
|
8
8
|
import AGConfirmModal from '../components/modals/AGConfirmModal.vue';
|
|
9
|
-
import AGLoadingModal from '../components/modals/AGLoadingModal.vue';
|
|
10
9
|
import type { UIComponent } from './UI';
|
|
11
10
|
|
|
12
11
|
export { UI, UIComponents, UIComponent };
|
|
@@ -20,7 +19,6 @@ export default definePlugin({
|
|
|
20
19
|
const defaultComponents = {
|
|
21
20
|
[UIComponents.AlertModal]: AGAlertModal,
|
|
22
21
|
[UIComponents.ConfirmModal]: AGConfirmModal,
|
|
23
|
-
[UIComponents.LoadingModal]: AGLoadingModal,
|
|
24
22
|
};
|
|
25
23
|
|
|
26
24
|
Object.entries({
|
package/src/utils/index.ts
CHANGED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<AGHeadlessInput ref="$input" :name="name" class="flex">
|
|
3
|
-
<AGHeadlessInputInput
|
|
4
|
-
v-bind="$attrs"
|
|
5
|
-
type="checkbox"
|
|
6
|
-
:class="{
|
|
7
|
-
'text-indigo-600 focus:ring-indigo-600': !$input?.errors,
|
|
8
|
-
'border-red-200 text-red-600 focus:ring-red-600': $input?.errors,
|
|
9
|
-
}"
|
|
10
|
-
/>
|
|
11
|
-
|
|
12
|
-
<div class="ml-2">
|
|
13
|
-
<AGHeadlessInputLabel v-if="$slots.default">
|
|
14
|
-
<slot />
|
|
15
|
-
</AGHeadlessInputLabel>
|
|
16
|
-
<AGHeadlessInputError class="text-sm text-red-600" />
|
|
17
|
-
</div>
|
|
18
|
-
</AGHeadlessInput>
|
|
19
|
-
</template>
|
|
20
|
-
|
|
21
|
-
<script setup lang="ts">
|
|
22
|
-
import { componentRef, stringProp } from '@/utils/vue';
|
|
23
|
-
|
|
24
|
-
import type { IAGHeadlessInput } from '@/components/headless/forms/AGHeadlessInput';
|
|
25
|
-
|
|
26
|
-
import AGHeadlessInput from '../headless/forms/AGHeadlessInput.vue';
|
|
27
|
-
import AGHeadlessInputError from '../headless/forms/AGHeadlessInputError.vue';
|
|
28
|
-
import AGHeadlessInputInput from '../headless/forms/AGHeadlessInputInput.vue';
|
|
29
|
-
import AGHeadlessInputLabel from '../headless/forms/AGHeadlessInputLabel.vue';
|
|
30
|
-
|
|
31
|
-
defineProps({ name: stringProp() });
|
|
32
|
-
defineOptions({ inheritAttrs: false });
|
|
33
|
-
|
|
34
|
-
const $input = componentRef<IAGHeadlessInput>();
|
|
35
|
-
</script>
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<AGModal :cancellable="false">
|
|
3
|
-
<AGMarkdown :text="renderedMessage" />
|
|
4
|
-
</AGModal>
|
|
5
|
-
</template>
|
|
6
|
-
|
|
7
|
-
<script setup lang="ts">
|
|
8
|
-
import { computed } from 'vue';
|
|
9
|
-
|
|
10
|
-
import { stringProp } from '@/utils/vue';
|
|
11
|
-
import { translateWithDefault } from '@/lang/utils';
|
|
12
|
-
|
|
13
|
-
import AGModal from './AGModal.vue';
|
|
14
|
-
|
|
15
|
-
import AGMarkdown from '../basic/AGMarkdown.vue';
|
|
16
|
-
|
|
17
|
-
const props = defineProps({ message: stringProp() });
|
|
18
|
-
const renderedMessage = computed(() => props.message ?? translateWithDefault('ui.loading', 'Loading...'));
|
|
19
|
-
</script>
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import type { JSError } from '@noeldemartin/utils';
|
|
2
|
-
|
|
3
|
-
import { defineServiceState } from '@/services';
|
|
4
|
-
|
|
5
|
-
export type ErrorSource = string | Error | JSError | unknown;
|
|
6
|
-
|
|
7
|
-
export interface ErrorReport {
|
|
8
|
-
title: string;
|
|
9
|
-
description?: string;
|
|
10
|
-
details?: string;
|
|
11
|
-
error?: Error | JSError | unknown;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface ErrorReportLog {
|
|
15
|
-
report: ErrorReport;
|
|
16
|
-
seen: boolean;
|
|
17
|
-
date: Date;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export default defineServiceState({
|
|
21
|
-
name: 'errors',
|
|
22
|
-
initialState: {
|
|
23
|
-
logs: [] as ErrorReportLog[],
|
|
24
|
-
startupErrors: [] as ErrorReport[],
|
|
25
|
-
},
|
|
26
|
-
computed: {
|
|
27
|
-
hasErrors: ({ logs }) => logs.length > 0,
|
|
28
|
-
hasNewErrors: ({ logs }) => logs.some((error) => !error.seen),
|
|
29
|
-
hasStartupErrors: ({ startupErrors }) => startupErrors.length > 0,
|
|
30
|
-
},
|
|
31
|
-
});
|
package/src/errors/Errors.ts
DELETED
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import { JSError, facade, isObject } from '@noeldemartin/utils';
|
|
2
|
-
|
|
3
|
-
import App from '@/services/App';
|
|
4
|
-
import ServiceBootError from '@/errors/ServiceBootError';
|
|
5
|
-
import UI from '@/ui/UI';
|
|
6
|
-
import { translate } from '@/lang/utils';
|
|
7
|
-
|
|
8
|
-
import Service from './Errors.state';
|
|
9
|
-
import type { ErrorReport, ErrorReportLog, ErrorSource } from './Errors.state';
|
|
10
|
-
|
|
11
|
-
export class ErrorsService extends Service {
|
|
12
|
-
|
|
13
|
-
public forceReporting: boolean = false;
|
|
14
|
-
private enabled: boolean = true;
|
|
15
|
-
|
|
16
|
-
public enable(): void {
|
|
17
|
-
this.enabled = true;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
public disable(): void {
|
|
21
|
-
this.enabled = false;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
public async inspect(error: ErrorSource | ErrorReport[]): Promise<void> {
|
|
25
|
-
const reports = Array.isArray(error) ? error : [await this.createErrorReport(error)];
|
|
26
|
-
|
|
27
|
-
// TODO open errors modal
|
|
28
|
-
reports;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
public async report(error: ErrorSource, message?: string): Promise<void> {
|
|
32
|
-
if (App.isDevelopment || App.isTesting) {
|
|
33
|
-
this.logError(error);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (!this.enabled) {
|
|
37
|
-
throw error;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (!App.isMounted) {
|
|
41
|
-
const startupError = await this.createStartupErrorReport(error);
|
|
42
|
-
|
|
43
|
-
if (startupError) {
|
|
44
|
-
this.setState({ startupErrors: this.startupErrors.concat(startupError) });
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const report = await this.createErrorReport(error);
|
|
51
|
-
const log: ErrorReportLog = {
|
|
52
|
-
report,
|
|
53
|
-
seen: false,
|
|
54
|
-
date: new Date(),
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
// TODO open error snackbar
|
|
58
|
-
UI.alert(message ?? 'Something went wrong, but it\'s not your fault! (look at the console for details)');
|
|
59
|
-
|
|
60
|
-
this.setState({ logs: [log].concat(this.logs) });
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
public see(report: ErrorReport): void {
|
|
64
|
-
this.setState({
|
|
65
|
-
logs: this.logs.map((log) => {
|
|
66
|
-
if (log.report !== report) {
|
|
67
|
-
return log;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return {
|
|
71
|
-
...log,
|
|
72
|
-
seen: true,
|
|
73
|
-
};
|
|
74
|
-
}),
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
public seeAll(): void {
|
|
79
|
-
this.setState({
|
|
80
|
-
logs: this.logs.map((log) => ({
|
|
81
|
-
...log,
|
|
82
|
-
seen: true,
|
|
83
|
-
})),
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
private logError(error: unknown): void {
|
|
88
|
-
// eslint-disable-next-line no-console
|
|
89
|
-
console.error(error);
|
|
90
|
-
|
|
91
|
-
if (isObject(error) && error.cause) {
|
|
92
|
-
this.logError(error.cause);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
private async createErrorReport(error: ErrorSource): Promise<ErrorReport> {
|
|
97
|
-
if (typeof error === 'string') {
|
|
98
|
-
return { title: error };
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (error instanceof Error || error instanceof JSError) {
|
|
102
|
-
return this.createErrorReportFromError(error);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return {
|
|
106
|
-
title: translate('errors.unknown'),
|
|
107
|
-
error,
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
private async createStartupErrorReport(error: ErrorSource): Promise<ErrorReport | null> {
|
|
112
|
-
if (error instanceof ServiceBootError) {
|
|
113
|
-
// Ignore second-order boot errors in order to have a cleaner startup crash screen.
|
|
114
|
-
return error.cause instanceof ServiceBootError ? null : this.createErrorReport(error.cause);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return this.createErrorReport(error);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
private createErrorReportFromError(error: Error | JSError, defaults: Partial<ErrorReport> = {}): ErrorReport {
|
|
121
|
-
return {
|
|
122
|
-
title: error.name,
|
|
123
|
-
description: error.message,
|
|
124
|
-
details: error.stack,
|
|
125
|
-
error,
|
|
126
|
-
...defaults,
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
export default facade(new ErrorsService());
|
package/src/errors/index.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { bootServices } from '@/services';
|
|
2
|
-
import { definePlugin } from '@/plugins';
|
|
3
|
-
|
|
4
|
-
import Errors from './Errors';
|
|
5
|
-
import { ErrorReport, ErrorReportLog, ErrorSource } from './Errors.state';
|
|
6
|
-
|
|
7
|
-
export { Errors, ErrorSource, ErrorReport, ErrorReportLog };
|
|
8
|
-
|
|
9
|
-
const services = { $errors: Errors };
|
|
10
|
-
|
|
11
|
-
export type ErrorsServices = typeof services;
|
|
12
|
-
|
|
13
|
-
export default definePlugin({
|
|
14
|
-
async install(app) {
|
|
15
|
-
await bootServices(app, services);
|
|
16
|
-
},
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
declare module '@/services' {
|
|
20
|
-
interface Services extends ErrorsServices {}
|
|
21
|
-
}
|
package/src/lang/utils.ts
DELETED
package/src/services/store.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { createPinia, defineStore, setActivePinia } from 'pinia';
|
|
2
|
-
import type { DefineStoreOptions, Pinia, StateTree, Store, _GettersTree } from 'pinia';
|
|
3
|
-
|
|
4
|
-
let _store: Pinia | null = null;
|
|
5
|
-
|
|
6
|
-
function initializePiniaStore(): Pinia {
|
|
7
|
-
if (!_store) {
|
|
8
|
-
_store = createPinia();
|
|
9
|
-
|
|
10
|
-
setActivePinia(_store);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
return _store;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function getPiniaStore(): Pinia {
|
|
17
|
-
return _store ?? initializePiniaStore();
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function defineServiceStore<Id extends string, S extends StateTree = {}, G extends _GettersTree<S> = {}, A = {}>(
|
|
21
|
-
name: Id,
|
|
22
|
-
options: Omit<DefineStoreOptions<Id, S, G, A>, 'id'>,
|
|
23
|
-
): Store<Id, S, G, A> {
|
|
24
|
-
initializePiniaStore();
|
|
25
|
-
|
|
26
|
-
return defineStore(name, options)();
|
|
27
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { objectWithout } from '@noeldemartin/utils';
|
|
2
|
-
import { computed, useAttrs } from 'vue';
|
|
3
|
-
import type { ComputedRef } from 'vue';
|
|
4
|
-
|
|
5
|
-
export function useInputAttrs(): [ComputedRef<{}>, ComputedRef<unknown>] {
|
|
6
|
-
const attrs = useAttrs();
|
|
7
|
-
const className = computed(() => attrs.class);
|
|
8
|
-
const inputAttrs = computed(() => objectWithout(attrs, 'class'));
|
|
9
|
-
|
|
10
|
-
return [inputAttrs, className];
|
|
11
|
-
}
|