@twinsearth/dsh-2xray 0.2.0 → 0.2.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.1 — 2026-10-29
4
+
5
+ ### 修复
6
+
7
+ - **设置页仍不显示**:Schemastery 改为优先通过 `createRequire(import.meta.url)` / profile 锚点解析文件后动态 import,绕开宿主模块加载器对裸 specifier 的受限解析;加载结果写入 `xray_status.settingsSchema`,可远程确认表单是否具备渲染条件。
8
+ - **侧边栏入口优先注册**:先注册 `sidebar.panellist`,再注册主面板;主面板失败不再连带隐藏入口。
9
+
3
10
  ## 0.2.0 — 2026-10-29
4
11
 
5
12
  ### 新增
package/index.js CHANGED
@@ -90,6 +90,7 @@ export function apply(ctx, rawConfig) {
90
90
  launcher,
91
91
  route,
92
92
  control,
93
+ schemaLoaded: Boolean(Config),
93
94
  logger,
94
95
  dataDir,
95
96
  })
package/lib/client.js CHANGED
@@ -246,19 +246,21 @@ window.__ModuleLoader__.load({
246
246
  }
247
247
 
248
248
  function apply(ctx) {
249
+ // Register the sidebar entry first: a failure in the main-panel
250
+ // registration must not hide the entry.
249
251
  ctx.effect(
250
252
  () =>
251
- ctx.slots.inject('main', () =>
252
- ctx.slots.register({ name: 'main', key: PANEL_ID }, XrayPage),
253
+ ctx.slots.inject('sidebar.panellist', () =>
254
+ ctx.slots.register({ name: 'sidebar.panellist', id: PANEL_ID, order: 1, label: () => '2Xray' }, GlobeIcon),
253
255
  ),
254
- 'dsh-2xray: main panel',
256
+ 'dsh-2xray: sidebar entry',
255
257
  )
256
258
  ctx.effect(
257
259
  () =>
258
- ctx.slots.inject('sidebar.panellist', () =>
259
- ctx.slots.register({ name: 'sidebar.panellist', id: PANEL_ID, order: 1, label: () => '2Xray' }, GlobeIcon),
260
+ ctx.slots.inject('main', () =>
261
+ ctx.slots.register({ name: 'main', key: PANEL_ID }, XrayPage),
260
262
  ),
261
- 'dsh-2xray: sidebar entry',
263
+ 'dsh-2xray: main panel',
262
264
  )
263
265
  }
264
266
 
package/lib/schema.js CHANGED
@@ -19,11 +19,6 @@ import { join } from 'node:path'
19
19
  import { pathToFileURL } from 'node:url'
20
20
 
21
21
  async function loadSchema() {
22
- try {
23
- return (await import('@deepseek-ai/schemastery')).default
24
- } catch {
25
- // local checkout / test: borrow the profile's copy
26
- }
27
22
  const home = process.env.USERPROFILE ?? process.env.HOME
28
23
  const anchors = home
29
24
  ? [
@@ -31,13 +26,29 @@ async function loadSchema() {
31
26
  join(home, '.dsh', 'profiles', 'web', 'package.json'),
32
27
  ]
33
28
  : []
34
- for (const anchor of anchors) {
35
- try {
29
+
30
+ // Prefer Node's own resolver: a bare `import()` inside a plugin may be served
31
+ // by the harness module loader, whose table does not necessarily contain a
32
+ // peer package such as Schemastery even though it sits in node_modules.
33
+ const attempts = [
34
+ () => {
35
+ const file = createRequire(import.meta.url).resolve('@deepseek-ai/schemastery')
36
+ return import(pathToFileURL(file).href)
37
+ },
38
+ ...anchors.map((anchor) => () => {
36
39
  const file = createRequire(anchor).resolve('@deepseek-ai/schemastery')
37
- const module = await import(pathToFileURL(file).href)
38
- return module.default ?? module
40
+ return import(pathToFileURL(file).href)
41
+ }),
42
+ () => import('@deepseek-ai/schemastery'),
43
+ ]
44
+
45
+ for (const attempt of attempts) {
46
+ try {
47
+ const module = await attempt()
48
+ const candidate = module?.default ?? module
49
+ if (typeof candidate === 'function' && typeof candidate.object === 'function') return candidate
39
50
  } catch {
40
- // try the next profile
51
+ // try the next resolution route
41
52
  }
42
53
  }
43
54
  throw new Error('@deepseek-ai/schemastery is not resolvable')
package/lib/supervisor.js CHANGED
@@ -86,6 +86,7 @@ export class Supervisor {
86
86
  #resolveBinaryFn
87
87
  #control
88
88
  #activeNode
89
+ #schemaLoaded = false
89
90
  #now
90
91
 
91
92
  constructor({
@@ -100,6 +101,7 @@ export class Supervisor {
100
101
  probes = {},
101
102
  resolveBinaryFn = resolveBinary,
102
103
  control,
104
+ schemaLoaded = false,
103
105
  now = () => Date.now(),
104
106
  }) {
105
107
  this.config = config
@@ -108,6 +110,7 @@ export class Supervisor {
108
110
  this.#launcher = launcher
109
111
  this.#route = route
110
112
  this.#control = control
113
+ this.#schemaLoaded = schemaLoaded
111
114
  this.#activeNode = config.activeNode
112
115
  this.#logger = logger
113
116
  this.dataDir = dataDir
@@ -646,6 +649,7 @@ export class Supervisor {
646
649
  detail: this.#detail ?? null,
647
650
  enabled: this.config.enabled,
648
651
  takeover: this.config.takeover,
652
+ settingsSchema: this.#schemaLoaded,
649
653
  nodes: this.config.nodes.map((node) => ({ id: node.id, tag: node.tag, address: node.address, port: node.port })),
650
654
  activeNode: this.#activeNode,
651
655
  control: this.#control ? { enabled: true, ...this.#control.snapshot() } : { enabled: false },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twinsearth/dsh-2xray",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "private": false,
5
5
  "description": "DSH-2Xray: supervises xray-core and routes DSH process-wide HTTP(S) traffic through its local inbound, with optional multi-node gRPC control plane",
6
6
  "type": "module",
@@ -45,12 +45,12 @@ test('the client bundle registers a main panel and a sidebar entry', async () =>
45
45
 
46
46
  assert.deepEqual(
47
47
  registrations.map((entry) => entry.definition.name),
48
- ['main', 'sidebar.panellist'],
48
+ ['sidebar.panellist', 'main'],
49
49
  )
50
- assert.equal(registrations[0].definition.key, 'xray')
51
- assert.equal(registrations[1].definition.id, 'xray')
52
- assert.equal(registrations[1].definition.order, 1)
53
- assert.equal(registrations[1].definition.label(), '2Xray')
54
- const icon = registrations[1].component()
50
+ assert.equal(registrations[0].definition.id, 'xray')
51
+ assert.equal(registrations[0].definition.order, 1)
52
+ assert.equal(registrations[0].definition.label(), '2Xray')
53
+ const icon = registrations[0].component()
55
54
  assert.equal(icon.type, 'svg')
55
+ assert.equal(registrations[1].definition.key, 'xray')
56
56
  })