@wexample/js-app 0.0.3 → 0.0.12

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/.wex/config.yml CHANGED
@@ -1,3 +1,3 @@
1
1
  global:
2
2
  name: js_app
3
- version: 0.0.3
3
+ version: 0.0.12
@@ -9,12 +9,13 @@ class AppWorkdir(JavascriptPackageWorkdir):
9
9
  from wexample_helpers.helpers.string import string_to_kebab_case
10
10
 
11
11
  raw_value = super().prepare_value(raw_value=raw_value)
12
+ name_config = self.get_runtime_config().search("global.name")
12
13
 
13
14
  def _build_remote_github(target: AppWorkdir) -> str:
14
- return f"git@github.com:wexample/{string_to_kebab_case(target.get_project_name())}.git"
15
+ return f"git@github.com:wexample/{string_to_kebab_case(name_config.get_str())}.git"
15
16
 
16
17
  def _build_remote_gitlab(target: AppWorkdir) -> str:
17
- return f"ssh://git@gitlab.wexample.com:4567/wexample-javascript/{string_to_kebab_case(target.get_project_name())}.git"
18
+ return f"ssh://git@gitlab.wexample.com:4567/wexample-javascript/{string_to_kebab_case(name_config.get_str())}.git"
18
19
 
19
20
  raw_value["git"] = {
20
21
  "main_branch": "main",
@@ -4,8 +4,6 @@ version = "0.0.1"
4
4
  description = "App manager entrypoint"
5
5
  requires-python = ">=3.10"
6
6
  dependencies = [
7
- "jinja2",
8
- "wexample-wex-addon-app==0.0.53",
9
7
  "wexample-wex-addon-dev-javascript==0.0.55",
10
8
  ]
11
9
 
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # js_app
1
+ # @wexample/js-app
2
2
 
3
- Version: 0.0.3
3
+ Version: 0.0.12
4
4
 
5
5
  ## Table of Contents
6
6
 
@@ -111,7 +111,7 @@ Free to use in both personal and commercial projects.
111
111
 
112
112
  ## Integration in the Suite
113
113
 
114
- This package is part of the **Wexample Suite** — a collection of high-quality Python packages designed to work seamlessly together.
114
+ This package is part of the Wexample Suite — a collection of high-quality, modular tools designed to work seamlessly together across multiple languages and environments.
115
115
 
116
116
  ### Related Packages
117
117
 
package/package.json CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
- "version": "0.0.3",
2
+ "version": "0.0.12",
3
3
  "type": "module",
4
4
  "name": "@wexample/js-app",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
+ },
8
+ "peerDependencies": {
9
+ "@wexample/js-helpers": "*"
7
10
  }
8
11
  }
@@ -0,0 +1,115 @@
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
+ }
@@ -0,0 +1,10 @@
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
+ }
@@ -0,0 +1,24 @@
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
+ }
@@ -0,0 +1,5 @@
1
+ import type MixinsService from '../Services/MixinsService';
2
+
3
+ export default interface ServicesRegistryInterface {
4
+ mixins?: MixinsService;
5
+ }
@@ -0,0 +1,87 @@
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
+ }
@@ -0,0 +1,12 @@
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[]];
package/tsconfig.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ESNext",
5
+ "moduleResolution": "node",
6
+ "strict": false,
7
+ "skipLibCheck": true,
8
+ "esModuleInterop": true,
9
+ "forceConsistentCasingInFileNames": true,
10
+ "declaration": false,
11
+ "declarationMap": false,
12
+ "isolatedModules": true,
13
+ "resolveJsonModule": true,
14
+ "baseUrl": ".",
15
+ "paths": {
16
+ "@wexample/js-app": ["./src"],
17
+ "@wexample/js-app/*": ["./src/*"],
18
+ "@wexample/js-helpers": ["../js-helpers/src"],
19
+ "@wexample/js-helpers/*": ["../js-helpers/src/*"],
20
+ "@wexample/js-pseudocode": ["../js-pseudocode/src"],
21
+ "@wexample/js-pseudocode/*": ["../js-pseudocode/src/*"]
22
+ }
23
+ },
24
+ "include": ["src"],
25
+ "exclude": ["dist", "node_modules"]
26
+ }
package/version.txt CHANGED
@@ -1 +1 @@
1
- 0.0.3
1
+ 0.0.12