stonyx 0.2.3-alpha.60 → 0.2.3-alpha.61

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/modules.js CHANGED
@@ -16,10 +16,29 @@ const modulePromises = {};
16
16
  // and this is the only question it needs answered: "is this name in the
17
17
  // manifest?" is exactly what `modulePromises` stopped being able to answer.
18
18
  //
19
- // Reassigned per `loadModules` call rather than accumulated, so it tracks the
20
- // manifest of the most recent load exactly as `modulePromises` tracks that
21
- // load's discovery — one derived fact, one lifetime, rather than a second
22
- // singleton drifting away from the first.
19
+ // Reassigned per `loadModules` call rather than accumulated, because only the
20
+ // current manifest can answer for the current boot — a name declared by an
21
+ // earlier load must not answer for this one.
22
+ //
23
+ // It is NOT symmetric with `modulePromises`, and the asymmetry is worth
24
+ // stating because the obvious reading of the line above is that it is.
25
+ // `modulePromises` has no reset: no `delete`, no reassignment, no clear, at
26
+ // any commit on `dev`. It accumulates the union of every `loadModules` call in
27
+ // the process while this set is replaced per call. Measured over three loads
28
+ // in one process at c87826b: load A declares and installs
29
+ // `@stonyx/probe-life`; load B declares it with the package absent, so
30
+ // discovery warns and `continue`s and registers nothing — and
31
+ // `waitForModule('probe-life')` still RESOLVES, off load A's stale entry.
32
+ // That is the "reports success for a module never loaded, silently" outcome
33
+ // the registration loop below calls worse than the hang; T23 forecloses that
34
+ // remedy WITHIN one call and no test sees across calls.
35
+ //
36
+ // Inherited, not introduced here — `modulePromises` was never reset at base
37
+ // 5693744 either — and deliberately not closed here, because giving it a
38
+ // per-call lifetime has a blast radius of its own and is filed separately.
39
+ // Production reachability is nil: one `loadModules` call site, guarded by the
40
+ // `Stonyx.instance` early return. The suite is held safe by the
41
+ // unique-module-name-per-test rule in this file's test header, not by this.
23
42
  let declaredModuleNames = new Set();
24
43
  // Configure module-specific logging
25
44
  function configureLog(chronicle, module, config) {
@@ -37,8 +56,9 @@ function initializeModule(moduleName, moduleClass, modules, initPromises) {
37
56
  // which is never created. Third of the three never-resolved paths named in
38
57
  // #120, and the one that scoping registration to `discovered` does NOT close,
39
58
  // because such a module IS discovered — measured, not reasoned: with only
40
- // the registration fix applied the suite read 180/1 with T24 the sole
41
- // failure. Resolving here is readiness BY DEFINITION — there is no `init()`,
59
+ // the registration fix applied the suite read 180 pass / 1 fail at f575a3c
60
+ // with T24 the sole failure. The SHA is the point; see the aggregate
61
+ // convention noted at the `declaredDependencies` union below. Resolving here is readiness BY DEFINITION — there is no `init()`,
42
62
  // so there is nothing to wait for — and that is what separates it from
43
63
  // resolving on a discovery `continue`, where there is no module to be ready
44
64
  // at all.
@@ -226,13 +246,18 @@ export default async function loadModules(config, rootPath, chronicle) {
226
246
  // change: hang becomes throw — and the throw names the right cause, because
227
247
  // `modulePromises` is no longer the app's declared list and `waitForModule`
228
248
  // below branches on that difference rather than asserting the old one. T13
229
- // pins both halves, and the PR body names them.
249
+ // pins the hang-becomes-throw change and the named cause; the PR body names
250
+ // them. ("Halves" is avoided here because T13 uses that word for its own
251
+ // input-domain split — the `devDependencies` half it covers and the
252
+ // `dependencies` half it hands to T23.)
230
253
  //
231
254
  // SCOPED DELIBERATELY, because the sentence above would otherwise read as a
232
255
  // closure statement for the whole never-settling-promise family and it is
233
256
  // not one. What it closes is names that FAIL discovery. A name that PASSES
234
- // discovery is registered, and its promise is resolved only once its own
235
- // `init()` has settled — so two discovered modules whose `init()`s await
257
+ // discovery is registered, and where its class HAS an `init()` its promise
258
+ // is resolved only once that `init()` has settled — the qualifier matters,
259
+ // because two of the three resolve sites named above settle without any
260
+ // `init()` running. So two discovered modules whose `init()`s await
236
261
  // each other through `waitForModule` are both registered, reach no resolve
237
262
  // site, and leave `await Promise.all(initPromises)` at the end of this
238
263
  // function pending forever. Measured on a cross-map mutual wait
@@ -242,6 +267,10 @@ export default async function loadModules(config, rootPath, chronicle) {
242
267
  // discovered, so it failed fast for the wrong reason
243
268
  // a57045e HUNG, 3001 ms
244
269
  // f575a3c HUNG, 3003 ms — scoping registration does not touch it
270
+ // c87826b HUNG, 3002 ms — re-measured at the head this ships from,
271
+ // because the rows above stop before three commits that
272
+ // touch this file and a reader at head cannot tell from a
273
+ // truncated table whether the row still applies
245
274
  // Rule 3 moves that shape from fail-fast to deadlock and nothing in this
246
275
  // file closes it.
247
276
  //
@@ -354,9 +383,10 @@ export async function waitForModule(moduleName) {
354
383
  const modulePromise = modulePromises[fullName];
355
384
  if (!modulePromise) {
356
385
  throw new Error(declaredModuleNames.has(fullName)
357
- ? `Could wait for module: ${fullName}. It IS declared in this project's dependencies, but the ` +
358
- 'loader did not load it: either it is not installed under node_modules, or its package.json ' +
359
- 'does not carry the "stonyx-module" keyword. loadModules warned which one at load time.'
386
+ ? `Could wait for module: ${fullName}. It IS declared in this project's dependencies or ` +
387
+ 'devDependencies, but the loader did not load it: either it is not installed under ' +
388
+ 'node_modules, or its package.json does not carry the "stonyx-module" keyword. ' +
389
+ 'loadModules warned which one at load time.'
360
390
  : `Could wait for module: ${fullName}. Module was not registered in project dependencies`);
361
391
  }
362
392
  await modulePromise.ready;
@@ -407,7 +407,8 @@ export function duplicateCoreMessage(foreign) {
407
407
  'de-duplicated union of both maps. What it then reports is not those packages\' own copies but ' +
408
408
  'the copy each of them would IMPORT: for every one it follows Node\'s ESM resolution walk up ' +
409
409
  'from that module\'s own directory and takes the FIRST copy that walk finds, whoever owns it — ' +
410
- 'including this app\'s own node_modules/stonyx, which no module declares. A copy no module\'s ' +
410
+ 'including this app\'s own node_modules/stonyx, which no module need declare. A copy no ' +
411
+ 'module\'s ' +
411
412
  'walk reaches first is not counted, and one further up a walk is hidden by a nearer one.',
412
413
  ].join('\n');
413
414
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stonyx",
3
- "version": "0.2.3-alpha.60",
3
+ "version": "0.2.3-alpha.61",
4
4
  "description": "Base stonyx framework module",
5
5
  "main": "dist/main.js",
6
6
  "types": "dist/main.d.ts",