stonyx 0.2.3-alpha.56 → 0.2.3-alpha.58

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
@@ -27,7 +27,7 @@ stonyx test # Bootstrap + run tests
27
27
  stonyx help # Show all available commands
28
28
  ```
29
29
 
30
- Stonyx reads `config/environment.ts` (preferred) or `config/environment.js`, initializes all `@stonyx/*` modules from your `devDependencies`, and runs your application.
30
+ Stonyx reads `config/environment.ts` (preferred) or `config/environment.js`, initializes all `@stonyx/*` modules declared in your `dependencies` or `devDependencies`, and runs your application.
31
31
 
32
32
  ## Documentation
33
33
 
package/dist/modules.js CHANGED
@@ -18,8 +18,24 @@ function configureLog(chronicle, module, config) {
18
18
  function initializeModule(moduleName, moduleClass, modules, initPromises) {
19
19
  const moduleInstance = new moduleClass();
20
20
  modules.push(moduleInstance);
21
- if (!moduleInstance.init)
21
+ // A module class with no `init()` has nothing to initialise, so it is ready
22
+ // the moment it is instantiated. Without this its registered promise dangles
23
+ // — the only resolve on this path lives inside the `init()` wrapper below,
24
+ // which is never created. Third of the three never-resolved paths named in
25
+ // #120, and the one that scoping registration to `discovered` does NOT close,
26
+ // because such a module IS discovered — measured, not reasoned: with only
27
+ // the registration fix applied the suite read 180/1 with T24 the sole
28
+ // failure. Resolving here is truthful, because the module really did load,
29
+ // and that is what separates it from resolving on a discovery `continue`,
30
+ // where there is no module to be ready.
31
+ //
32
+ // `?.` for the reason the resolve below carries it: `initializeModule` is
33
+ // also reached from the standalone path as `initializeModule(projectName, …)`,
34
+ // and `projectName` is never a key in `modulePromises`.
35
+ if (!moduleInstance.init) {
36
+ modulePromises[moduleName]?.resolve();
22
37
  return;
38
+ }
23
39
  initPromises.push((async () => {
24
40
  await moduleInstance.init();
25
41
  modulePromises[moduleName]?.resolve();
@@ -78,17 +94,35 @@ export default async function loadModules(config, rootPath, chronicle) {
78
94
  const modules = [];
79
95
  const initPromises = [];
80
96
  const rootPackage = await readFile(`${rootPath}/package.json`, { json: true });
81
- const dependencies = (rootPackage.devDependencies || {});
97
+ // DISCOVERY SOURCE (abofs/stonyx#106 rule 3).
98
+ //
99
+ // This local used to be named `dependencies` while holding `devDependencies`
100
+ // and nothing else. That name is the ruled root cause of #106: every reader
101
+ // of this function — including its own docs — believed discovery scanned
102
+ // both maps, because the identifier said so. It did not. An installed stonyx
103
+ // module declared in `dependencies` was invisible, which is precisely how the
104
+ // published fleet pins the core, so the loader could not see its own
105
+ // siblings.
106
+ //
107
+ // Rule 3 widens the source to the DE-DUPLICATED union of both maps. Object
108
+ // spread IS the de-duplication and is load-bearing: a module declared in both
109
+ // maps collapses to one key, so it is registered once and instantiated once.
110
+ // A bare `[ ...Object.keys(deps), ...Object.keys(devDeps) ]` concat produces a
111
+ // byte-identical suite aggregate while instantiating such a module twice and
112
+ // running its `init()` twice — T22's instance-count assertion owns that kill.
113
+ const declaredDependencies = {
114
+ ...(rootPackage.dependencies || {}),
115
+ ...(rootPackage.devDependencies || {}),
116
+ };
82
117
  const projectName = typeof rootPackage.name === 'string' ? rootPackage.name : '';
83
118
  // Expose rootPath to public configuration
84
119
  config.rootPath = rootPath;
85
- const moduleDependencies = Object.keys(dependencies).filter((moduleName) => moduleName.startsWith('@stonyx/'));
86
- // Setup module promises prior to initialization
87
- for (const moduleName of moduleDependencies) {
88
- const promise = {};
89
- modulePromises[moduleName] = promise;
90
- promise.ready = new Promise(resolve => promise.resolve = resolve);
91
- }
120
+ // The `@stonyx/` prefix filter STAYS (#106 keeps it explicitly). Widening it
121
+ // to `@` — or dropping it — makes the loader warn on every non-Stonyx
122
+ // dependency and leaves their `modulePromises` entries permanently
123
+ // unresolved. T3 owns that kill. What is a module is decided below, by the
124
+ // `stonyx-module` keyword; this is only a name test that bounds the scan.
125
+ const moduleDependencies = Object.keys(declaredDependencies).filter((moduleName) => moduleName.startsWith('@stonyx/'));
92
126
  // DISCOVERY, hoisted above the pre-flight.
93
127
  //
94
128
  // The pre-flight must check exactly what the loader loads, and no more.
@@ -119,6 +153,41 @@ export default async function loadModules(config, rootPath, chronicle) {
119
153
  keywords,
120
154
  });
121
155
  }
156
+ // Setup module promises prior to initialization — over `discovered`, NOT
157
+ // over `moduleDependencies`.
158
+ //
159
+ // This loop used to run above discovery and consume the declared list. That
160
+ // was already the shape at base, but rule 3 widened what the declared list
161
+ // contains, and the widening turned a fast named throw into an unbounded
162
+ // boot hang. A name registered here but never resolved leaves
163
+ // `waitForModule` pending forever; discovery `continue`s past two kinds of
164
+ // name without reaching either of this file's two resolve sites — a manifest
165
+ // that is not there, and a package without the `stonyx-module` keyword.
166
+ // `@stonyx/logs` ships without that keyword and is an ordinary `dependencies`
167
+ // entry, so this is a shape the fleet actually has.
168
+ //
169
+ // name in `dependencies`, not installed, another module awaits it
170
+ // base 5693744 throws "…was not registered in project dependencies", 3 ms
171
+ // a57045e one console.warn, then loadModules never settles
172
+ //
173
+ // Registering only what discovery admitted restores the fail-fast for both,
174
+ // and for `devDependencies`-declared names too — that half is INHERITED, not
175
+ // introduced here, and it is a real behaviour change: hang becomes throw.
176
+ // T13 pins it, and the PR body names it.
177
+ //
178
+ // The alternative — resolving in each `continue` branch — also removes the
179
+ // hang and is worse: `waitForModule` would report success for a module that
180
+ // was never loaded, silently. T23 asserts the named rejection precisely so
181
+ // that remedy cannot pass.
182
+ //
183
+ // Ordering is safe: nothing between here and the old site touches
184
+ // `modulePromises`, discovery only reads manifests, and registration still
185
+ // completes before the first `init()` can run.
186
+ for (const { name: moduleName } of discovered) {
187
+ const promise = {};
188
+ modulePromises[moduleName] = promise;
189
+ promise.ready = new Promise(resolve => promise.resolve = resolve);
190
+ }
122
191
  // Pre-flight: invariant I1, "one core". Before ANY module entry point is
123
192
  // imported, because the point of the check is the module that would NOT
124
193
  // throw — it would load, initialise, and register its hooks on a second
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stonyx",
3
- "version": "0.2.3-alpha.56",
3
+ "version": "0.2.3-alpha.58",
4
4
  "description": "Base stonyx framework module",
5
5
  "main": "dist/main.js",
6
6
  "types": "dist/main.d.ts",