@zfdx123/dsh-hooks-ordering 1.0.5 → 1.0.6

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.
Files changed (2) hide show
  1. package/client.js +114 -13
  2. package/package.json +1 -1
package/client.js CHANGED
@@ -346,6 +346,73 @@
346
346
  return style
347
347
  }
348
348
 
349
+ /**
350
+ * The name this plugin's settings entry actually has, resolved at runtime
351
+ * from the descriptors the host returns.
352
+ *
353
+ * It CANNOT be a constant. The settings namespace is the **loader entry
354
+ * id**, which belongs to whoever wrote the row that mounted this plugin,
355
+ * not to the plugin: the aggregator's `cordis.patch.yml` uses
356
+ * `dsh-plugin-hooks-ordering`, while this package's own patch uses
357
+ * `hooks-ordering`. Hardcoding the latter against the former produced
358
+ *
359
+ * no settings entry is named "hooks-ordering" (describe() returned: …, dsh-plugin-hooks-ordering, …)
360
+ *
361
+ * and the write path was refused outright:
362
+ *
363
+ * settings/rejected: No configurable plugin entry "hooks-ordering"
364
+ *
365
+ * Matching on the descriptor's own `ns` removes the guesswork: whatever id
366
+ * the mounting row has, this is the entry it produced.
367
+ */
368
+ var resolvedNs = null
369
+
370
+ /**
371
+ * Normalise a Remote answer to `{ ok, value }` / `{ ok: false, reason }`.
372
+ *
373
+ * The 0.1.7 Remote protocol answers every call with a `RemoteResult`
374
+ * envelope and the client proxy hands that envelope through as-is, so a
375
+ * caller that reads fields off the answer directly sees `undefined` while
376
+ * the wire traffic is healthy. A plain value is accepted too, so this
377
+ * keeps working if a future runtime unwraps for us.
378
+ *
379
+ * @param answer - whatever the Remote call resolved to.
380
+ * @returns the unwrapped outcome.
381
+ */
382
+ function unwrapRemote(answer) {
383
+ if (answer !== null && typeof answer === 'object' && typeof answer.ok === 'boolean') {
384
+ if (answer.ok === false) {
385
+ var failure = answer.error
386
+ var reason =
387
+ failure && typeof failure === 'object'
388
+ ? String(failure.code || 'remote error') + ': ' + String(failure.message || '')
389
+ : String(failure || 'remote error')
390
+ return { ok: false, reason: reason }
391
+ }
392
+ return { ok: true, value: answer.value }
393
+ }
394
+ return { ok: true, value: answer }
395
+ }
396
+
397
+ /**
398
+ * @param list - descriptor list from `describe()`.
399
+ * @returns the descriptor for this plugin, or undefined.
400
+ */
401
+ function pickEntry(list) {
402
+ // Fast path: the row used this package's own documented id.
403
+ var direct = list.find(function (entry) {
404
+ return entry && entry.ns === NS
405
+ })
406
+ if (direct !== undefined) return direct
407
+ // Otherwise take the entry that was mounting this plugin created.
408
+ // `dsh-plugin-hooks-ordering` is the shipped aggregator row; the suffix
409
+ // test also covers a user's own row name.
410
+ var suffixed = list.filter(function (entry) {
411
+ return entry && typeof entry.ns === 'string' && entry.ns.slice(-NS.length) === NS
412
+ })
413
+ return suffixed.length === 1 ? suffixed[0] : undefined
414
+ }
415
+
349
416
  // ── the settings page ──────────────────────────────────────────────────
350
417
 
351
418
  /**
@@ -396,6 +463,12 @@
396
463
  */
397
464
  function createSettingsSource(ctx) {
398
465
  var remote = ctx && ctx.remote && ctx.remote.settings
466
+ /**
467
+ * The name this plugin's settings entry actually has is resolved at
468
+ * runtime by {@link pickEntry} into the module-level `resolvedNs`; see
469
+ * the note there for why it cannot be a constant.
470
+ */
471
+
399
472
  /**
400
473
  * Why the form has no data. Kept as a concrete, user-visible string:
401
474
  * "not available" on its own made a wiring fault indistinguishable from
@@ -431,17 +504,27 @@
431
504
  return remote.describe()
432
505
  })
433
506
  .then(function (answer) {
434
- var list = answer && Array.isArray(answer.namespaces) ? answer.namespaces : null
435
- if (list === null) {
436
- return unavailableBecause('describe() returned no namespaces array')
507
+ // A Remote call answers with a `RemoteResult` envelope
508
+ // (`{ok:true, value}` / `{ok:false, error}`) and the client proxy
509
+ // does NOT unwrap it: reading `answer.namespaces` directly got
510
+ // `undefined`, which surfaced as the misleading
511
+ // "describe() returned no namespaces array" while the network
512
+ // exchange was perfectly healthy (ok:true, 20 namespaces).
513
+ var result = unwrapRemote(answer)
514
+ if (result.ok === false) {
515
+ return unavailableBecause('describe() failed: ' + result.reason)
437
516
  }
438
- var found
439
- for (var i = 0; i < list.length; i += 1) {
440
- if (list[i] && list[i].ns === NS) {
441
- found = list[i]
442
- break
443
- }
517
+ var value = result.value
518
+ if (value === null || value === undefined) {
519
+ return unavailableBecause('describe() returned no value')
520
+ }
521
+ var list = Array.isArray(value.namespaces) ? value.namespaces : null
522
+ if (list === null) {
523
+ return unavailableBecause(
524
+ 'describe() value has no namespaces array (keys: ' + Object.keys(value).join(', ') + ')',
525
+ )
444
526
  }
527
+ var found = pickEntry(list)
445
528
  if (found === undefined) {
446
529
  var seen = list
447
530
  .map(function (entry) {
@@ -450,9 +533,14 @@
450
533
  .filter(Boolean)
451
534
  .join(', ')
452
535
  return unavailableBecause(
453
- 'no settings entry is named "' + NS + '" (describe() returned: ' + (seen || 'nothing') + ')',
536
+ 'no settings entry for this plugin (looked for "' +
537
+ NS +
538
+ '"; describe() returned: ' +
539
+ (seen || 'nothing') +
540
+ ')',
454
541
  )
455
542
  }
543
+ resolvedNs = found.ns
456
544
  return {
457
545
  status: 'ready',
458
546
  available: true,
@@ -482,12 +570,22 @@
482
570
  }
483
571
  // The revision is read fresh immediately before the write so a page
484
572
  // left open across another edit reports a conflict instead of
485
- // silently overwriting it.
573
+ // silently overwriting it. `read()` also refreshes `resolvedNs`.
486
574
  return read().then(function (snap) {
487
575
  if (!snap.available) {
488
576
  throw new Error(snap.reason || 'this plugin has no settings entry in the active profile')
489
577
  }
490
- return remote.mutate(NS, ops, snap.revision)
578
+ if (resolvedNs === null) {
579
+ throw new Error('this plugin did not resolve its settings entry id')
580
+ }
581
+ // The write answers with a RemoteResult envelope as well; unwrap
582
+ // it so a refusal surfaces as a real message instead of looking
583
+ // like a successful save.
584
+ return remote.mutate(resolvedNs, ops, snap.revision).then(function (answer) {
585
+ var result = unwrapRemote(answer)
586
+ if (result.ok === false) throw new Error(result.reason)
587
+ return result.value
588
+ })
491
589
  })
492
590
  },
493
591
  /**
@@ -503,7 +601,8 @@
503
601
  if (!events || typeof events.$on !== 'function') return function () {}
504
602
  try {
505
603
  return events.$on('settings/document-updated', function (ns) {
506
- if (ns === undefined || ns === NS) listener()
604
+ // Match the resolved entry id, not the package-relative name.
605
+ if (ns === undefined || ns === resolvedNs || ns === NS) listener()
507
606
  })
508
607
  } catch (error) {
509
608
  // A missing gateway listener must not take the page down.
@@ -796,6 +895,8 @@
796
895
  exports.apply = apply
797
896
  exports.inject = inject
798
897
  exports.NS = NS
898
+ exports.pickEntry = pickEntry
899
+ exports.unwrapRemote = unwrapRemote
799
900
  exports.CLASS = CLASS
800
901
  exports.CSS = CSS
801
902
  exports.ZH = zh
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zfdx123/dsh-hooks-ordering",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "description": "钩子排序:为 Cordis 的 waterfall / serial 钩子提供确定性的 before/after 排序,让互相独立的插件能声明彼此的先后关系(拓扑排序、环检测、可配置控制集与设置页)。",
5
5
  "keywords": [
6
6
  "cordis",