@wexample/js-app 0.0.25 → 0.0.29

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @wexample/js-app
2
2
 
3
- Version: 0.0.25
3
+ Version: 0.0.29
4
4
 
5
5
  ## Table of Contents
6
6
 
@@ -0,0 +1,19 @@
1
+ import AsyncConstructor from '@wexample/js-helpers/Common/AsyncConstructor';
2
+ import type ServicesRegistryInterface from '../Interfaces/ServicesRegistryInterface.js';
3
+ import type { ServiceDefinition } from '../Types/AppServiceTypes.js';
4
+ import type AppService from './AppService.js';
5
+ type ReadyCallback = (() => void) | (() => Promise<void>);
6
+ export default class App extends AsyncConstructor {
7
+ services: ServicesRegistryInterface;
8
+ constructor(readyCallback?: ReadyCallback, globalName?: string);
9
+ protected beforeReady(): Promise<void>;
10
+ getServices(): ServiceDefinition[];
11
+ loadServices(services: ServiceDefinition[]): AppService[];
12
+ loadAndInitServices(services: ServiceDefinition[]): Promise<unknown>;
13
+ getServicesAndDependencies(services: ServiceDefinition[]): ServiceDefinition[];
14
+ getService(name: string | {
15
+ serviceName: string;
16
+ }): AppService;
17
+ }
18
+ export {};
19
+ //# sourceMappingURL=App.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/Common/App.ts"],"names":[],"mappings":"AACA,OAAO,gBAAgB,MAAM,8CAA8C,CAAC;AAE5E,OAAO,KAAK,yBAAyB,MAAM,4CAA4C,CAAC;AACxF,OAAO,KAAK,EAAyB,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAC5F,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAC;AAE9C,KAAK,aAAa,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;AAE1D,MAAM,CAAC,OAAO,OAAO,GAAI,SAAQ,gBAAgB;IACxC,QAAQ,EAAE,yBAAyB,CAAM;gBAEpC,aAAa,CAAC,EAAE,aAAa,EAAE,UAAU,GAAE,MAAc;cAiCrD,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5C,WAAW,IAAI,iBAAiB,EAAE;IAIlC,YAAY,CAAC,QAAQ,EAAE,iBAAiB,EAAE,GAAG,UAAU,EAAE;IA0BnD,mBAAmB,CAAC,QAAQ,EAAE,iBAAiB,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAY1E,0BAA0B,CAAC,QAAQ,EAAE,iBAAiB,EAAE,GAAG,iBAAiB,EAAE;IAkB9E,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,UAAU;CAK/D"}
@@ -0,0 +1,82 @@
1
+ import MixinsService from '@wexample/js-app/Services/MixinsService';
2
+ import AsyncConstructor from '@wexample/js-helpers/Common/AsyncConstructor';
3
+ import { arrayUnique } from '@wexample/js-helpers/Helper/Array';
4
+ export default class App extends AsyncConstructor {
5
+ constructor(readyCallback, globalName = 'app') {
6
+ super();
7
+ this.services = {};
8
+ window[globalName] = this;
9
+ const doc = window.document;
10
+ const run = async () => {
11
+ // Allow children to perform setup before sealing.
12
+ await this.beforeReady();
13
+ // Every core properties has been set,
14
+ // block any try to add extra property.
15
+ this.seal();
16
+ // Execute ready callbacks.
17
+ await this.readyComplete();
18
+ readyCallback && (await readyCallback());
19
+ };
20
+ const readyState = doc.readyState;
21
+ // Document has been parsed.
22
+ // Allows running after loaded event.
23
+ if (['complete', 'loaded', 'interactive'].indexOf(readyState) !== -1) {
24
+ this.defer(run);
25
+ }
26
+ else {
27
+ doc.addEventListener('DOMContentLoaded', run);
28
+ }
29
+ }
30
+ // Hook for children: executed after DOM is ready but before seal().
31
+ async beforeReady() {
32
+ await this.loadAndInitServices(this.getServices());
33
+ }
34
+ getServices() {
35
+ return [MixinsService];
36
+ }
37
+ loadServices(services) {
38
+ services = this.getServicesAndDependencies(services);
39
+ const instances = [];
40
+ services.forEach((service) => {
41
+ let serviceClass;
42
+ let serviceArgs = [];
43
+ if (Array.isArray(service)) {
44
+ serviceClass = service[0];
45
+ serviceArgs = service[1];
46
+ }
47
+ else {
48
+ serviceClass = service;
49
+ }
50
+ const name = serviceClass.serviceName;
51
+ if (!this.services[name]) {
52
+ this.services[name] = new serviceClass(this, ...serviceArgs);
53
+ instances.push(this.services[name]);
54
+ }
55
+ });
56
+ return instances;
57
+ }
58
+ async loadAndInitServices(services) {
59
+ const loadedServices = this.loadServices(services);
60
+ return this.services.mixins.invokeUntilComplete('hookInit', 'app', [], undefined, loadedServices);
61
+ }
62
+ getServicesAndDependencies(services) {
63
+ services.forEach((serviceDef) => {
64
+ let serviceClass;
65
+ if (Array.isArray(serviceDef)) {
66
+ serviceClass = serviceDef[0];
67
+ }
68
+ else {
69
+ serviceClass = serviceDef;
70
+ }
71
+ if (serviceClass.dependencies) {
72
+ services = [...services, ...this.getServicesAndDependencies(serviceClass.dependencies)];
73
+ }
74
+ });
75
+ return arrayUnique(services);
76
+ }
77
+ getService(name) {
78
+ name = typeof name === 'string' ? name : name.serviceName;
79
+ return this.services[name];
80
+ }
81
+ }
82
+ //# sourceMappingURL=App.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"App.js","sourceRoot":"","sources":["../../src/Common/App.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import AsyncConstructor from '@wexample/js-helpers/Common/AsyncConstructor';
2
+ import type App from './App.js';
3
+ export default class extends AsyncConstructor {
4
+ protected readonly app: App;
5
+ constructor(app: App);
6
+ }
7
+ //# sourceMappingURL=AppChild.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AppChild.d.ts","sourceRoot":"","sources":["../../src/Common/AppChild.ts"],"names":[],"mappings":"AAAA,OAAO,gBAAgB,MAAM,8CAA8C,CAAC;AAC5E,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAEhC,MAAM,CAAC,OAAO,MAAO,SAAQ,gBAAgB;IAC/B,SAAS,CAAC,QAAQ,CAAC,GAAG,EAAE,GAAG;gBAAR,GAAG,EAAE,GAAG;CAKxC"}
@@ -0,0 +1,9 @@
1
+ import AsyncConstructor from '@wexample/js-helpers/Common/AsyncConstructor';
2
+ export default class extends AsyncConstructor {
3
+ constructor(app) {
4
+ super();
5
+ this.app = app;
6
+ this.app = app;
7
+ }
8
+ }
9
+ //# sourceMappingURL=AppChild.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AppChild.js","sourceRoot":"","sources":["../../src/Common/AppChild.ts"],"names":[],"mappings":"AAAA,OAAO,gBAAgB,MAAM,8CAA8C,CAAC;AAG5E,MAAM,CAAC,OAAO,MAAO,SAAQ,gBAAgB;IAC3C,YAA+B,GAAQ;QACrC,KAAK,EAAE,CAAC;QADqB,QAAG,GAAH,GAAG,CAAK;QAGrC,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;IACjB,CAAC;CACF"}
@@ -0,0 +1,17 @@
1
+ import type { ServiceDefinition } from '../Types/AppServiceTypes.js';
2
+ import type App from './App.js';
3
+ import AppChild from './AppChild.js';
4
+ export default abstract class AppService extends AppChild {
5
+ static LOAD_STATUS_COMPLETE: string;
6
+ static LOAD_STATUS_WAIT: string;
7
+ app: App;
8
+ static dependencies: ServiceDefinition[];
9
+ static serviceName: string;
10
+ registerHooks(): {
11
+ app?: Record<string, unknown>;
12
+ page?: Record<string, unknown>;
13
+ renderNode?: Record<string, unknown>;
14
+ };
15
+ registerMethods(_object: unknown, _group: string): {};
16
+ }
17
+ //# sourceMappingURL=AppService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AppService.d.ts","sourceRoot":"","sources":["../../src/Common/AppService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,QAAQ,MAAM,eAAe,CAAC;AAErC,MAAM,CAAC,OAAO,CAAC,QAAQ,OAAO,UAAW,SAAQ,QAAQ;IACvD,OAAc,oBAAoB,SAAc;IAChD,OAAc,gBAAgB,SAAU;IAEjC,GAAG,EAAE,GAAG,CAAC;IAChB,OAAc,YAAY,EAAE,iBAAiB,EAAE,CAAM;IACrD,OAAc,WAAW,EAAE,MAAM,CAAC;IAElC,aAAa,IAAI;QACf,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC9B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC/B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACtC;IAID,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM;CAGjD"}
@@ -0,0 +1,14 @@
1
+ import AppChild from './AppChild.js';
2
+ class AppService extends AppChild {
3
+ registerHooks() {
4
+ return {};
5
+ }
6
+ registerMethods(_object, _group) {
7
+ return {};
8
+ }
9
+ }
10
+ AppService.LOAD_STATUS_COMPLETE = 'complete';
11
+ AppService.LOAD_STATUS_WAIT = 'wait';
12
+ AppService.dependencies = [];
13
+ export default AppService;
14
+ //# sourceMappingURL=AppService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AppService.js","sourceRoot":"","sources":["../../src/Common/AppService.ts"],"names":[],"mappings":"AAEA,OAAO,QAAQ,MAAM,eAAe,CAAC;AAErC,MAA8B,UAAW,SAAQ,QAAQ;IAQvD,aAAa;QAKX,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,eAAe,CAAC,OAAgB,EAAE,MAAc;QAC9C,OAAO,EAAE,CAAC;IACZ,CAAC;;AAjBa,+BAAoB,GAAG,UAAU,CAAC;AAClC,2BAAgB,GAAG,MAAM,CAAC;AAG1B,uBAAY,GAAwB,EAAE,CAAC;eALzB,UAAU"}
@@ -0,0 +1,5 @@
1
+ import type MixinsService from '../Services/MixinsService.js';
2
+ export default interface ServicesRegistryInterface {
3
+ mixins?: MixinsService;
4
+ }
5
+ //# sourceMappingURL=ServicesRegistryInterface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ServicesRegistryInterface.d.ts","sourceRoot":"","sources":["../../src/Interfaces/ServicesRegistryInterface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,aAAa,MAAM,8BAA8B,CAAC;AAE9D,MAAM,CAAC,OAAO,WAAW,yBAAyB;IAChD,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ServicesRegistryInterface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ServicesRegistryInterface.js","sourceRoot":"","sources":["../../src/Interfaces/ServicesRegistryInterface.ts"],"names":[],"mappings":""}
@@ -0,0 +1,20 @@
1
+ import AppService from '@wexample/js-app/Common/AppService';
2
+ export default class MixinsService extends AppService {
3
+ static serviceName: string;
4
+ /**
5
+ * Execute a hook until all ext do not return false.
6
+ * Useful to manage order when processing : an ext can wait for
7
+ * another one to be executed.
8
+ *
9
+ * The pre-last arg of callback will be a registry of ext statuses.
10
+ * The last arg of callback well be a next() method in case of async operation.
11
+ *
12
+ * @param method
13
+ * @param args
14
+ * @param group
15
+ * @param timeoutLimit
16
+ * @param services
17
+ */
18
+ invokeUntilComplete(method: string, group?: string, args?: unknown[], timeoutLimit?: number, services?: AppService[]): Promise<boolean>;
19
+ }
20
+ //# sourceMappingURL=MixinsService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MixinsService.d.ts","sourceRoot":"","sources":["../../src/Services/MixinsService.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,oCAAoC,CAAC;AAG5D,MAAM,CAAC,OAAO,OAAO,aAAc,SAAQ,UAAU;IACnD,OAAc,WAAW,SAAY;IAErC;;;;;;;;;;;;;OAaG;IACG,mBAAmB,CACvB,MAAM,EAAE,MAAM,EACd,KAAK,GAAE,MAAc,EACrB,IAAI,GAAE,OAAO,EAAO,EACpB,YAAY,GAAE,MAAa,EAC3B,QAAQ,GAAE,UAAU,EAAqD,GACxE,OAAO,CAAC,OAAO,CAAC;CA4DpB"}
@@ -0,0 +1,67 @@
1
+ import AppService from '@wexample/js-app/Common/AppService';
2
+ class MixinsService extends AppService {
3
+ /**
4
+ * Execute a hook until all ext do not return false.
5
+ * Useful to manage order when processing : an ext can wait for
6
+ * another one to be executed.
7
+ *
8
+ * The pre-last arg of callback will be a registry of ext statuses.
9
+ * The last arg of callback well be a next() method in case of async operation.
10
+ *
11
+ * @param method
12
+ * @param args
13
+ * @param group
14
+ * @param timeoutLimit
15
+ * @param services
16
+ */
17
+ async invokeUntilComplete(method, group = 'app', args = [], timeoutLimit = 2000, services = Object.values(this.app.services)) {
18
+ const errorTrace = [];
19
+ let loops = 0;
20
+ const loopsLimit = 100;
21
+ const registry = {};
22
+ while (true) {
23
+ const service = services.shift();
24
+ if (!service) {
25
+ break;
26
+ }
27
+ const currentName = service.constructor.serviceName;
28
+ const timeout = setTimeout(() => {
29
+ const message = [
30
+ `Mixins invocation timeout on method "${method}", stopping at "${currentName}".`,
31
+ `Registry: ${JSON.stringify(registry)}.`,
32
+ ].join(' ');
33
+ throw new Error(message);
34
+ }, timeoutLimit);
35
+ const hooks = service.registerHooks();
36
+ const hook = hooks?.[group]?.[method];
37
+ if (loops++ > loopsLimit) {
38
+ const message = [
39
+ `Stopping more than ${loops} recursions during services invocation on method "${method}", stopping at ${currentName}.`,
40
+ `Trace: ${errorTrace.join(' -> ') || 'none'}.`,
41
+ `Registry: ${JSON.stringify(registry)}.`,
42
+ ].join(' ');
43
+ throw new Error(message);
44
+ }
45
+ else if (loops > loopsLimit - 10) {
46
+ errorTrace.push(service);
47
+ }
48
+ if (typeof hook === 'function') {
49
+ const argsLocal = args.concat([registry]);
50
+ registry[currentName] = await hook.apply(service, argsLocal);
51
+ }
52
+ if (registry[currentName] === undefined) {
53
+ registry[currentName] = AppService.LOAD_STATUS_COMPLETE;
54
+ }
55
+ // "wait" says to retry after processing other services.
56
+ if (registry[currentName] === AppService.LOAD_STATUS_WAIT) {
57
+ // Enqueue again.
58
+ services.push(service);
59
+ }
60
+ clearTimeout(timeout);
61
+ }
62
+ return true;
63
+ }
64
+ }
65
+ MixinsService.serviceName = 'mixins';
66
+ export default MixinsService;
67
+ //# sourceMappingURL=MixinsService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MixinsService.js","sourceRoot":"","sources":["../../src/Services/MixinsService.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,oCAAoC,CAAC;AAG5D,MAAqB,aAAc,SAAQ,UAAU;IAGnD;;;;;;;;;;;;;OAaG;IACH,KAAK,CAAC,mBAAmB,CACvB,MAAc,EACd,QAAgB,KAAK,EACrB,OAAkB,EAAE,EACpB,eAAuB,IAAI,EAC3B,WAAyB,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAiB;QAEzE,MAAM,UAAU,GAAiB,EAAE,CAAC;QACpC,IAAI,KAAK,GAAW,CAAC,CAAC;QACtB,MAAM,UAAU,GAAW,GAAG,CAAC;QAC/B,MAAM,QAAQ,GAA4B,EAAE,CAAC;QAE7C,OAAO,IAAI,EAAE,CAAC;YACZ,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,EAAE,CAAC;YACjC,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,MAAM;YACR,CAAC;YAED,MAAM,WAAW,GAAI,OAAO,CAAC,WAAqC,CAAC,WAAW,CAAC;YAC/E,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC9B,MAAM,OAAO,GAAG;oBACd,wCAAwC,MAAM,mBAAmB,WAAW,IAAI;oBAChF,aAAa,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG;iBACzC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAEZ,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;YAC3B,CAAC,EAAE,YAAY,CAAC,CAAC;YAEjB,MAAM,KAAK,GAAG,OAAO,CAAC,aAAa,EAAyD,CAAC;YAC7F,MAAM,IAAI,GAAG,KAAK,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;YAEtC,IAAI,KAAK,EAAE,GAAG,UAAU,EAAE,CAAC;gBACzB,MAAM,OAAO,GAAG;oBACd,sBAAsB,KAAK,qDAAqD,MAAM,kBAAkB,WAAW,GAAG;oBACtH,UAAU,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG;oBAC9C,aAAa,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG;iBACzC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAEZ,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;YAC3B,CAAC;iBAAM,IAAI,KAAK,GAAG,UAAU,GAAG,EAAE,EAAE,CAAC;gBACnC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC3B,CAAC;YAED,IAAI,OAAO,IAAI,KAAK,UAAU,EAAE,CAAC;gBAC/B,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;gBAC1C,QAAQ,CAAC,WAAW,CAAC,GAAG,MAAO,IAA4C,CAAC,KAAK,CAC/E,OAAO,EACP,SAAS,CACV,CAAC;YACJ,CAAC;YAED,IAAI,QAAQ,CAAC,WAAW,CAAC,KAAK,SAAS,EAAE,CAAC;gBACxC,QAAQ,CAAC,WAAW,CAAC,GAAG,UAAU,CAAC,oBAAoB,CAAC;YAC1D,CAAC;YAED,wDAAwD;YACxD,IAAI,QAAQ,CAAC,WAAW,CAAC,KAAK,UAAU,CAAC,gBAAgB,EAAE,CAAC;gBAC1D,iBAAiB;gBACjB,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACzB,CAAC;YAED,YAAY,CAAC,OAAO,CAAC,CAAC;QACxB,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;;AAjFa,yBAAW,GAAG,QAAQ,CAAC;eADlB,aAAa"}
@@ -0,0 +1,8 @@
1
+ import type App from '../Common/App.js';
2
+ import type AppService from '../Common/AppService.js';
3
+ export type AppServiceConstructor = (new (app: App, ...args: unknown[]) => AppService) & {
4
+ serviceName: string;
5
+ dependencies?: ServiceDefinition[];
6
+ };
7
+ export type ServiceDefinition = AppServiceConstructor | [AppServiceConstructor, unknown[]];
8
+ //# sourceMappingURL=AppServiceTypes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AppServiceTypes.d.ts","sourceRoot":"","sources":["../../src/Types/AppServiceTypes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,kBAAkB,CAAC;AACxC,OAAO,KAAK,UAAU,MAAM,yBAAyB,CAAC;AAEtD,MAAM,MAAM,qBAAqB,GAAG,CAAC,KACnC,GAAG,EAAE,GAAG,EACR,GAAG,IAAI,EAAE,OAAO,EAAE,KACf,UAAU,CAAC,GAAG;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,iBAAiB,EAAE,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,qBAAqB,GAAG,CAAC,qBAAqB,EAAE,OAAO,EAAE,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=AppServiceTypes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AppServiceTypes.js","sourceRoot":"","sources":["../../src/Types/AppServiceTypes.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,23 +1,30 @@
1
1
  {
2
- "version": "0.0.25",
2
+ "version": "0.0.29",
3
3
  "type": "module",
4
4
  "name": "@wexample/js-app",
5
5
  "files": [
6
- "src"
6
+ "dist"
7
7
  ],
8
+ "scripts": {
9
+ "build": "tsc",
10
+ "prepublishOnly": "npm run build"
11
+ },
12
+ "devDependencies": {
13
+ "typescript": "^5.9.3"
14
+ },
8
15
  "publishConfig": {
9
16
  "access": "public"
10
17
  },
11
18
  "exports": {
12
19
  "./*": {
13
- "types": "./src/*",
14
- "default": "./src/*"
20
+ "types": "./dist/*.d.ts",
21
+ "default": "./dist/*.js"
15
22
  }
16
23
  },
17
24
  "typesVersions": {
18
25
  "*": {
19
26
  "*": [
20
- "src/*"
27
+ "dist/*"
21
28
  ]
22
29
  }
23
30
  },
package/src/Common/App.ts DELETED
@@ -1,115 +0,0 @@
1
- import MixinsService from '@wexample/js-app/Services/MixinsService';
2
- import AsyncConstructor from '@wexample/js-helpers/Common/AsyncConstructor';
3
- import { arrayUnique } from '@wexample/js-helpers/Helper/Array';
4
- import type ServicesRegistryInterface from '../Interfaces/ServicesRegistryInterface';
5
- import type { AppServiceConstructor, ServiceDefinition } from '../Types/AppServiceTypes';
6
- import type AppService from './AppService';
7
-
8
- type ReadyCallback = (() => void) | (() => Promise<void>);
9
-
10
- export default class App extends AsyncConstructor {
11
- public services: ServicesRegistryInterface = {};
12
-
13
- constructor(readyCallback?: ReadyCallback, globalName: string = 'app') {
14
- super();
15
-
16
- window[globalName] = this;
17
-
18
- const doc = window.document;
19
-
20
- const run = async () => {
21
- // Allow children to perform setup before sealing.
22
- await this.beforeReady();
23
-
24
- // Every core properties has been set,
25
- // block any try to add extra property.
26
- this.seal();
27
-
28
- // Execute ready callbacks.
29
- await this.readyComplete();
30
-
31
- readyCallback && (await readyCallback());
32
- };
33
-
34
- const readyState = doc.readyState;
35
-
36
- // Document has been parsed.
37
- // Allows running after loaded event.
38
- if (['complete', 'loaded', 'interactive'].indexOf(readyState) !== -1) {
39
- this.defer(run);
40
- } else {
41
- doc.addEventListener('DOMContentLoaded', run);
42
- }
43
- }
44
-
45
- // Hook for children: executed after DOM is ready but before seal().
46
- protected async beforeReady(): Promise<void> {
47
- await this.loadAndInitServices(this.getServices());
48
- }
49
-
50
- getServices(): ServiceDefinition[] {
51
- return [MixinsService];
52
- }
53
-
54
- loadServices(services: ServiceDefinition[]): AppService[] {
55
- services = this.getServicesAndDependencies(services);
56
- const instances = [];
57
-
58
- services.forEach((service: ServiceDefinition) => {
59
- let serviceClass: AppServiceConstructor;
60
- let serviceArgs: unknown[] = [];
61
-
62
- if (Array.isArray(service)) {
63
- serviceClass = service[0];
64
- serviceArgs = service[1];
65
- } else {
66
- serviceClass = service;
67
- }
68
-
69
- const name = serviceClass.serviceName;
70
-
71
- if (!this.services[name]) {
72
- this.services[name] = new serviceClass(this, ...serviceArgs);
73
- instances.push(this.services[name]);
74
- }
75
- });
76
-
77
- return instances;
78
- }
79
-
80
- async loadAndInitServices(services: ServiceDefinition[]): Promise<unknown> {
81
- const loadedServices = this.loadServices(services);
82
-
83
- return this.services.mixins.invokeUntilComplete(
84
- 'hookInit',
85
- 'app',
86
- [],
87
- undefined,
88
- loadedServices
89
- );
90
- }
91
-
92
- getServicesAndDependencies(services: ServiceDefinition[]): ServiceDefinition[] {
93
- services.forEach((serviceDef: ServiceDefinition) => {
94
- let serviceClass: AppServiceConstructor;
95
-
96
- if (Array.isArray(serviceDef)) {
97
- serviceClass = serviceDef[0];
98
- } else {
99
- serviceClass = serviceDef;
100
- }
101
-
102
- if (serviceClass.dependencies) {
103
- services = [...services, ...this.getServicesAndDependencies(serviceClass.dependencies)];
104
- }
105
- });
106
-
107
- return arrayUnique(services) as ServiceDefinition[];
108
- }
109
-
110
- getService(name: string | { serviceName: string }): AppService {
111
- name = typeof name === 'string' ? name : name.serviceName;
112
-
113
- return this.services[name];
114
- }
115
- }
@@ -1,10 +0,0 @@
1
- import AsyncConstructor from '@wexample/js-helpers/Common/AsyncConstructor';
2
- import type App from './App';
3
-
4
- export default class extends AsyncConstructor {
5
- constructor(protected readonly app: App) {
6
- super();
7
-
8
- this.app = app;
9
- }
10
- }
@@ -1,24 +0,0 @@
1
- import type { ServiceDefinition } from '../Types/AppServiceTypes';
2
- import type App from './App';
3
- import AppChild from './AppChild';
4
-
5
- export default abstract class AppService extends AppChild {
6
- public static LOAD_STATUS_COMPLETE = 'complete';
7
- public static LOAD_STATUS_WAIT = 'wait';
8
-
9
- public app: App;
10
- public static dependencies: ServiceDefinition[] = [];
11
- public static serviceName: string;
12
-
13
- registerHooks(): {
14
- app?: Record<string, unknown>;
15
- page?: Record<string, unknown>;
16
- renderNode?: Record<string, unknown>;
17
- } {
18
- return {};
19
- }
20
-
21
- registerMethods(_object: unknown, _group: string) {
22
- return {};
23
- }
24
- }
@@ -1,5 +0,0 @@
1
- import type MixinsService from '../Services/MixinsService';
2
-
3
- export default interface ServicesRegistryInterface {
4
- mixins?: MixinsService;
5
- }
@@ -1,87 +0,0 @@
1
- import AppService from '@wexample/js-app/Common/AppService';
2
- import type { AppServiceConstructor } from '../Types/AppServiceTypes';
3
-
4
- export default class MixinsService extends AppService {
5
- public static serviceName = 'mixins';
6
-
7
- /**
8
- * Execute a hook until all ext do not return false.
9
- * Useful to manage order when processing : an ext can wait for
10
- * another one to be executed.
11
- *
12
- * The pre-last arg of callback will be a registry of ext statuses.
13
- * The last arg of callback well be a next() method in case of async operation.
14
- *
15
- * @param method
16
- * @param args
17
- * @param group
18
- * @param timeoutLimit
19
- * @param services
20
- */
21
- async invokeUntilComplete(
22
- method: string,
23
- group: string = 'app',
24
- args: unknown[] = [],
25
- timeoutLimit: number = 2000,
26
- services: AppService[] = Object.values(this.app.services) as AppService[]
27
- ): Promise<boolean> {
28
- const errorTrace: AppService[] = [];
29
- let loops: number = 0;
30
- const loopsLimit: number = 100;
31
- const registry: Record<string, unknown> = {};
32
-
33
- while (true) {
34
- const service = services.shift();
35
- if (!service) {
36
- break;
37
- }
38
-
39
- const currentName = (service.constructor as AppServiceConstructor).serviceName;
40
- const timeout = setTimeout(() => {
41
- const message = [
42
- `Mixins invocation timeout on method "${method}", stopping at "${currentName}".`,
43
- `Registry: ${JSON.stringify(registry)}.`,
44
- ].join(' ');
45
-
46
- throw new Error(message);
47
- }, timeoutLimit);
48
-
49
- const hooks = service.registerHooks() as Record<string, Record<string, unknown>> | undefined;
50
- const hook = hooks?.[group]?.[method];
51
-
52
- if (loops++ > loopsLimit) {
53
- const message = [
54
- `Stopping more than ${loops} recursions during services invocation on method "${method}", stopping at ${currentName}.`,
55
- `Trace: ${errorTrace.join(' -> ') || 'none'}.`,
56
- `Registry: ${JSON.stringify(registry)}.`,
57
- ].join(' ');
58
-
59
- throw new Error(message);
60
- } else if (loops > loopsLimit - 10) {
61
- errorTrace.push(service);
62
- }
63
-
64
- if (typeof hook === 'function') {
65
- const argsLocal = args.concat([registry]);
66
- registry[currentName] = await (hook as (...hookArgs: unknown[]) => unknown).apply(
67
- service,
68
- argsLocal
69
- );
70
- }
71
-
72
- if (registry[currentName] === undefined) {
73
- registry[currentName] = AppService.LOAD_STATUS_COMPLETE;
74
- }
75
-
76
- // "wait" says to retry after processing other services.
77
- if (registry[currentName] === AppService.LOAD_STATUS_WAIT) {
78
- // Enqueue again.
79
- services.push(service);
80
- }
81
-
82
- clearTimeout(timeout);
83
- }
84
-
85
- return true;
86
- }
87
- }
@@ -1,12 +0,0 @@
1
- import type App from '../Common/App';
2
- import type AppService from '../Common/AppService';
3
-
4
- export type AppServiceConstructor = (new (
5
- app: App,
6
- ...args: unknown[]
7
- ) => AppService) & {
8
- serviceName: string;
9
- dependencies?: ServiceDefinition[];
10
- };
11
-
12
- export type ServiceDefinition = AppServiceConstructor | [AppServiceConstructor, unknown[]];