dshmarket 1.66.31 → 1.66.32
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/lib/regions.js +28 -0
- package/lib/registry.js +22 -6
- package/lib/types/regions.d.ts +20 -0
- package/package.json +1 -1
- package/src/regions.ts +28 -0
- package/src/registry.ts +22 -6
package/lib/regions.js
CHANGED
|
@@ -107,6 +107,34 @@ const ROUTES = {
|
|
|
107
107
|
],
|
|
108
108
|
},
|
|
109
109
|
};
|
|
110
|
+
/**
|
|
111
|
+
* Every catalog source worth trying for a region: its own list, plus the
|
|
112
|
+
* catalog's published package on that region's own registry when the list
|
|
113
|
+
* does not already name it.
|
|
114
|
+
*
|
|
115
|
+
* The catalog hangs off a SECOND, independently blockable host (#891), and
|
|
116
|
+
* the probe that picks a region measures only the npm registry. A network
|
|
117
|
+
* where npm answers fast and GitHub Pages is blocked therefore measures
|
|
118
|
+
* `global` — whose one source is that very host — and since the region is
|
|
119
|
+
* decided once and persisted, nothing ever reconsiders: "catalog fetch
|
|
120
|
+
* failed" forever, on a machine whose registry is serving the catalog as a
|
|
121
|
+
* package all along.
|
|
122
|
+
*
|
|
123
|
+
* Appending the package closes that blind spot without asking the probe to
|
|
124
|
+
* learn about the second host, without revisiting the decision, and without
|
|
125
|
+
* sending anyone to another region's mirror: it rides the registry the probe
|
|
126
|
+
* already proved reachable. Last, not first — for everyone the official
|
|
127
|
+
* domain serves, this stays a source that is never contacted.
|
|
128
|
+
*/
|
|
129
|
+
export function catalogSourcesFor(region) {
|
|
130
|
+
const routes = routesFor(region);
|
|
131
|
+
const sources = [...routes.catalog];
|
|
132
|
+
const served = sources.some(source => source.kind === 'npm'
|
|
133
|
+
&& source.registry === routes.npmRegistry && source.pkg === CATALOG_PACKAGE);
|
|
134
|
+
if (!served)
|
|
135
|
+
sources.push({ kind: 'npm', registry: routes.npmRegistry, pkg: CATALOG_PACKAGE });
|
|
136
|
+
return sources;
|
|
137
|
+
}
|
|
110
138
|
/** Read an environment override, treating blank as unset. */
|
|
111
139
|
function override(env, name) {
|
|
112
140
|
const raw = env[name];
|
package/lib/registry.js
CHANGED
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
* fresh on every request. See `loadRegistry` for why there is nothing
|
|
4
4
|
* behind it any more.
|
|
5
5
|
*/
|
|
6
|
+
import { logEvent } from './log.js';
|
|
6
7
|
import { configuredProxy, marketFetch } from './net.js';
|
|
7
8
|
import { catalogFromPackage } from './catalog-npm.js';
|
|
8
|
-
import { activeRegion, routesFor } from './regions.js';
|
|
9
|
+
import { activeRegion, catalogSourcesFor, routesFor } from './regions.js';
|
|
9
10
|
/**
|
|
10
11
|
* Category ids for one catalog entry, de-duplicated in declaration order.
|
|
11
12
|
*
|
|
@@ -166,8 +167,23 @@ export async function loadRegistry(region = activeRegion()) {
|
|
|
166
167
|
// the FIRST request the market makes, so a mirror that has gone down must
|
|
167
168
|
// mean a slow market rather than an empty one — the list ends at the
|
|
168
169
|
// address that has always worked.
|
|
169
|
-
|
|
170
|
+
//
|
|
171
|
+
// The list runs one entry past the region's own sources on purpose (#891):
|
|
172
|
+
// the region probe measures the npm registry, so it cannot see a network
|
|
173
|
+
// where npm is fast and the catalog's host is blocked — and the region is
|
|
174
|
+
// decided once and persisted. The catalog's published package, on the
|
|
175
|
+
// registry the probe just proved reachable, is the repair that needs
|
|
176
|
+
// neither the probe nor the decision to change.
|
|
177
|
+
const ownSources = routesFor(region).catalog.length;
|
|
178
|
+
const catalogSources = catalogSourcesFor(region);
|
|
179
|
+
for (const [sourceIndex, source] of catalogSources.entries()) {
|
|
170
180
|
const key = sourceKey(source);
|
|
181
|
+
const servedBy = (data) => {
|
|
182
|
+
if (sourceIndex >= ownSources) {
|
|
183
|
+
logEvent('warn', 'registry', `catalog: the ${region} sources did not answer; read the catalog package from ${key} instead (#891)`);
|
|
184
|
+
}
|
|
185
|
+
return data;
|
|
186
|
+
};
|
|
171
187
|
// Two attempts each. A catalog fetch crossing a long, lossy path fails
|
|
172
188
|
// transiently often enough that one retry is worth more than the second
|
|
173
189
|
// or two it costs — and with nothing behind this call any more, a
|
|
@@ -183,12 +199,12 @@ export async function loadRegistry(region = activeRegion()) {
|
|
|
183
199
|
const { version, data } = await catalogFromPackage(source.registry, source.pkg, reusable?.version ?? undefined);
|
|
184
200
|
// `data === null` means the published version is the one in hand.
|
|
185
201
|
if (data === null && reusable !== null)
|
|
186
|
-
return reusable.data;
|
|
202
|
+
return servedBy(reusable.data);
|
|
187
203
|
if (data === null)
|
|
188
204
|
throw new Error('the catalog package reported no change with nothing to reuse');
|
|
189
205
|
const parsed = asRegistry(data);
|
|
190
206
|
served = { key, etag: null, modified: null, version, data: parsed };
|
|
191
|
-
return parsed;
|
|
207
|
+
return servedBy(parsed);
|
|
192
208
|
}
|
|
193
209
|
// ETag first: it is exact, while a date has one-second resolution and
|
|
194
210
|
// a catalog republished twice within the same second would validate
|
|
@@ -221,7 +237,7 @@ export async function loadRegistry(region = activeRegion()) {
|
|
|
221
237
|
// otherwise surface as a confusing parse error on an empty body.
|
|
222
238
|
if (reusable === null)
|
|
223
239
|
throw new Error('the catalog answered "not modified" with nothing to revalidate');
|
|
224
|
-
return reusable.data;
|
|
240
|
+
return servedBy(reusable.data);
|
|
225
241
|
}
|
|
226
242
|
if (!res.ok)
|
|
227
243
|
throw new Error(`HTTP ${String(res.status)}`);
|
|
@@ -229,7 +245,7 @@ export async function loadRegistry(region = activeRegion()) {
|
|
|
229
245
|
served = {
|
|
230
246
|
key, etag: res.headers.get('etag'), modified: res.headers.get('last-modified'), version: null, data,
|
|
231
247
|
};
|
|
232
|
-
return data;
|
|
248
|
+
return servedBy(data);
|
|
233
249
|
}
|
|
234
250
|
catch (error) {
|
|
235
251
|
last = error;
|
package/lib/types/regions.d.ts
CHANGED
|
@@ -71,6 +71,26 @@ export interface RegionRoutes {
|
|
|
71
71
|
*/
|
|
72
72
|
catalog: CatalogSource[];
|
|
73
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* Every catalog source worth trying for a region: its own list, plus the
|
|
76
|
+
* catalog's published package on that region's own registry when the list
|
|
77
|
+
* does not already name it.
|
|
78
|
+
*
|
|
79
|
+
* The catalog hangs off a SECOND, independently blockable host (#891), and
|
|
80
|
+
* the probe that picks a region measures only the npm registry. A network
|
|
81
|
+
* where npm answers fast and GitHub Pages is blocked therefore measures
|
|
82
|
+
* `global` — whose one source is that very host — and since the region is
|
|
83
|
+
* decided once and persisted, nothing ever reconsiders: "catalog fetch
|
|
84
|
+
* failed" forever, on a machine whose registry is serving the catalog as a
|
|
85
|
+
* package all along.
|
|
86
|
+
*
|
|
87
|
+
* Appending the package closes that blind spot without asking the probe to
|
|
88
|
+
* learn about the second host, without revisiting the decision, and without
|
|
89
|
+
* sending anyone to another region's mirror: it rides the registry the probe
|
|
90
|
+
* already proved reachable. Last, not first — for everyone the official
|
|
91
|
+
* domain serves, this stays a source that is never contacted.
|
|
92
|
+
*/
|
|
93
|
+
export declare function catalogSourcesFor(region: Region): CatalogSource[];
|
|
74
94
|
/**
|
|
75
95
|
* Normalize a user-maintained prefix, or reject it.
|
|
76
96
|
*
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dshmarket",
|
|
3
3
|
"description": "Visual plugin market inside DeepSeek Harness — browse, search, and one-click install community plugins. · DSH 可视化插件市场:逛一逛,点一下,装好。",
|
|
4
|
-
"version": "1.66.
|
|
4
|
+
"version": "1.66.32",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"types": "lib/types/index.d.ts",
|
package/src/regions.ts
CHANGED
|
@@ -157,6 +157,34 @@ const ROUTES: Record<Region, RegionRoutes> = {
|
|
|
157
157
|
},
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
+
/**
|
|
161
|
+
* Every catalog source worth trying for a region: its own list, plus the
|
|
162
|
+
* catalog's published package on that region's own registry when the list
|
|
163
|
+
* does not already name it.
|
|
164
|
+
*
|
|
165
|
+
* The catalog hangs off a SECOND, independently blockable host (#891), and
|
|
166
|
+
* the probe that picks a region measures only the npm registry. A network
|
|
167
|
+
* where npm answers fast and GitHub Pages is blocked therefore measures
|
|
168
|
+
* `global` — whose one source is that very host — and since the region is
|
|
169
|
+
* decided once and persisted, nothing ever reconsiders: "catalog fetch
|
|
170
|
+
* failed" forever, on a machine whose registry is serving the catalog as a
|
|
171
|
+
* package all along.
|
|
172
|
+
*
|
|
173
|
+
* Appending the package closes that blind spot without asking the probe to
|
|
174
|
+
* learn about the second host, without revisiting the decision, and without
|
|
175
|
+
* sending anyone to another region's mirror: it rides the registry the probe
|
|
176
|
+
* already proved reachable. Last, not first — for everyone the official
|
|
177
|
+
* domain serves, this stays a source that is never contacted.
|
|
178
|
+
*/
|
|
179
|
+
export function catalogSourcesFor(region: Region): CatalogSource[] {
|
|
180
|
+
const routes = routesFor(region)
|
|
181
|
+
const sources = [...routes.catalog]
|
|
182
|
+
const served = sources.some(source => source.kind === 'npm'
|
|
183
|
+
&& source.registry === routes.npmRegistry && source.pkg === CATALOG_PACKAGE)
|
|
184
|
+
if (!served) sources.push({ kind: 'npm', registry: routes.npmRegistry, pkg: CATALOG_PACKAGE })
|
|
185
|
+
return sources
|
|
186
|
+
}
|
|
187
|
+
|
|
160
188
|
/** Read an environment override, treating blank as unset. */
|
|
161
189
|
function override(env: NodeJS.ProcessEnv, name: string): string | null {
|
|
162
190
|
const raw = env[name]
|
package/src/registry.ts
CHANGED
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
* behind it any more.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import { logEvent } from './log.ts'
|
|
7
8
|
import { configuredProxy, marketFetch } from './net.ts'
|
|
8
9
|
import { catalogFromPackage } from './catalog-npm.ts'
|
|
9
|
-
import { activeRegion, routesFor, type CatalogSource, type Region } from './regions.ts'
|
|
10
|
+
import { activeRegion, catalogSourcesFor, routesFor, type CatalogSource, type Region } from './regions.ts'
|
|
10
11
|
|
|
11
12
|
export interface RegistryPlugin {
|
|
12
13
|
name: string
|
|
@@ -245,8 +246,23 @@ export async function loadRegistry(region: Region = activeRegion()): Promise<Reg
|
|
|
245
246
|
// the FIRST request the market makes, so a mirror that has gone down must
|
|
246
247
|
// mean a slow market rather than an empty one — the list ends at the
|
|
247
248
|
// address that has always worked.
|
|
248
|
-
|
|
249
|
+
//
|
|
250
|
+
// The list runs one entry past the region's own sources on purpose (#891):
|
|
251
|
+
// the region probe measures the npm registry, so it cannot see a network
|
|
252
|
+
// where npm is fast and the catalog's host is blocked — and the region is
|
|
253
|
+
// decided once and persisted. The catalog's published package, on the
|
|
254
|
+
// registry the probe just proved reachable, is the repair that needs
|
|
255
|
+
// neither the probe nor the decision to change.
|
|
256
|
+
const ownSources = routesFor(region).catalog.length
|
|
257
|
+
const catalogSources = catalogSourcesFor(region)
|
|
258
|
+
for (const [sourceIndex, source] of catalogSources.entries()) {
|
|
249
259
|
const key = sourceKey(source)
|
|
260
|
+
const servedBy = <T>(data: T): T => {
|
|
261
|
+
if (sourceIndex >= ownSources) {
|
|
262
|
+
logEvent('warn', 'registry', `catalog: the ${region} sources did not answer; read the catalog package from ${key} instead (#891)`)
|
|
263
|
+
}
|
|
264
|
+
return data
|
|
265
|
+
}
|
|
250
266
|
// Two attempts each. A catalog fetch crossing a long, lossy path fails
|
|
251
267
|
// transiently often enough that one retry is worth more than the second
|
|
252
268
|
// or two it costs — and with nothing behind this call any more, a
|
|
@@ -263,11 +279,11 @@ export async function loadRegistry(region: Region = activeRegion()): Promise<Reg
|
|
|
263
279
|
source.registry, source.pkg, reusable?.version ?? undefined,
|
|
264
280
|
)
|
|
265
281
|
// `data === null` means the published version is the one in hand.
|
|
266
|
-
if (data === null && reusable !== null) return reusable.data
|
|
282
|
+
if (data === null && reusable !== null) return servedBy(reusable.data)
|
|
267
283
|
if (data === null) throw new Error('the catalog package reported no change with nothing to reuse')
|
|
268
284
|
const parsed = asRegistry(data)
|
|
269
285
|
served = { key, etag: null, modified: null, version, data: parsed }
|
|
270
|
-
return parsed
|
|
286
|
+
return servedBy(parsed)
|
|
271
287
|
}
|
|
272
288
|
// ETag first: it is exact, while a date has one-second resolution and
|
|
273
289
|
// a catalog republished twice within the same second would validate
|
|
@@ -300,7 +316,7 @@ export async function loadRegistry(region: Region = activeRegion()): Promise<Reg
|
|
|
300
316
|
// Guarded anyway: answering a 304 with nothing to reuse would
|
|
301
317
|
// otherwise surface as a confusing parse error on an empty body.
|
|
302
318
|
if (reusable === null) throw new Error('the catalog answered "not modified" with nothing to revalidate')
|
|
303
|
-
return reusable.data
|
|
319
|
+
return servedBy(reusable.data)
|
|
304
320
|
}
|
|
305
321
|
if (!res.ok) throw new Error(`HTTP ${String(res.status)}`)
|
|
306
322
|
const data = asRegistry(JSON.parse(new TextDecoder().decode(
|
|
@@ -309,7 +325,7 @@ export async function loadRegistry(region: Region = activeRegion()): Promise<Reg
|
|
|
309
325
|
served = {
|
|
310
326
|
key, etag: res.headers.get('etag'), modified: res.headers.get('last-modified'), version: null, data,
|
|
311
327
|
}
|
|
312
|
-
return data
|
|
328
|
+
return servedBy(data)
|
|
313
329
|
} catch (error) {
|
|
314
330
|
last = error
|
|
315
331
|
failures.push({
|