shennian 0.1.27 → 0.1.28

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.
@@ -3,15 +3,12 @@ export declare const SERVERS: Record<Region, {
3
3
  url: string;
4
4
  label: string;
5
5
  }>;
6
- export type ServerProbe = {
7
- region: Region;
8
- url: string;
9
- label: string;
10
- latencyMs: number | null;
11
- };
12
6
  /**
13
- * Probe both servers, auto-select the fastest reachable one.
14
- * Returns the chosen server URL.
7
+ * Detect the best server region and return its URL.
8
+ *
9
+ * Strategy: timezone as primary signal, IP geolocation as supplement.
10
+ * - Chinese timezone → cn immediately (covers servers with UTC too via IP fallback).
11
+ * - Non-Chinese timezone → try IP to detect if physically in China.
15
12
  */
16
13
  export declare function detectAndChooseServer(): Promise<string>;
17
14
  export declare function urlToRegion(url: string): Region;
@@ -1 +1 @@
1
- {"version":3,"file":"region.d.ts","sourceRoot":"","sources":["../../src/region.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,MAAM,GAAG,IAAI,GAAG,QAAQ,CAAA;AAEpC,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAGlE,CAAA;AAkBD,MAAM,MAAM,WAAW,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAA;AAkBlG;;;GAGG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,MAAM,CAAC,CAa7D;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAElD"}
1
+ {"version":3,"file":"region.d.ts","sourceRoot":"","sources":["../../src/region.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,MAAM,GAAG,IAAI,GAAG,QAAQ,CAAA;AAEpC,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAGlE,CAAA;AA6CD;;;;;;GAMG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,MAAM,CAAC,CAyC7D;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAElD"}
@@ -3,14 +3,29 @@ export const SERVERS = {
3
3
  cn: { url: 'https://shennian.net', label: 'China (shennian.net)' },
4
4
  global: { url: 'https://shennian.ai', label: 'Global (shennian.ai)' },
5
5
  };
6
- const REGIONS = ['cn', 'global'];
7
- async function measureLatency(url, timeoutMs = 5000) {
6
+ const CN_TIMEZONES = [
7
+ 'Asia/Shanghai', 'Asia/Chongqing', 'Asia/Chungking',
8
+ 'Asia/Harbin', 'Asia/Urumqi', 'PRC',
9
+ ];
10
+ function inferRegionByTimezone() {
11
+ try {
12
+ const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
13
+ if (CN_TIMEZONES.includes(tz))
14
+ return 'cn';
15
+ }
16
+ catch { /* Intl unavailable */ }
17
+ return 'global';
18
+ }
19
+ async function detectRegionByIP(timeoutMs = 1500) {
8
20
  const controller = new AbortController();
9
21
  const timer = setTimeout(() => controller.abort(), timeoutMs);
10
- const start = performance.now();
11
22
  try {
12
- await fetch(`${url}/health`, { signal: controller.signal });
13
- return Math.round(performance.now() - start);
23
+ const res = await Promise.any([
24
+ fetch(`${SERVERS.cn.url}/geo`, { signal: controller.signal }),
25
+ fetch(`${SERVERS.global.url}/geo`, { signal: controller.signal }),
26
+ ]);
27
+ const data = await res.json();
28
+ return data.country === 'CN' ? 'cn' : 'global';
14
29
  }
15
30
  catch {
16
31
  return null;
@@ -19,37 +34,62 @@ async function measureLatency(url, timeoutMs = 5000) {
19
34
  clearTimeout(timer);
20
35
  }
21
36
  }
22
- async function probeAll() {
23
- const results = await Promise.all(REGIONS.map(async (region) => {
24
- const { url, label } = SERVERS[region];
25
- const latencyMs = await measureLatency(url);
26
- return { region, url, label, latencyMs };
27
- }));
28
- return results.sort((a, b) => {
29
- if (a.latencyMs === null && b.latencyMs === null)
30
- return 0;
31
- if (a.latencyMs === null)
32
- return 1;
33
- if (b.latencyMs === null)
34
- return -1;
35
- return a.latencyMs - b.latencyMs;
36
- });
37
+ async function verifyReachable(url, timeoutMs = 3000) {
38
+ const controller = new AbortController();
39
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
40
+ try {
41
+ await fetch(`${url}/health`, { signal: controller.signal });
42
+ return true;
43
+ }
44
+ catch {
45
+ return false;
46
+ }
47
+ finally {
48
+ clearTimeout(timer);
49
+ }
37
50
  }
38
51
  /**
39
- * Probe both servers, auto-select the fastest reachable one.
40
- * Returns the chosen server URL.
52
+ * Detect the best server region and return its URL.
53
+ *
54
+ * Strategy: timezone as primary signal, IP geolocation as supplement.
55
+ * - Chinese timezone → cn immediately (covers servers with UTC too via IP fallback).
56
+ * - Non-Chinese timezone → try IP to detect if physically in China.
41
57
  */
42
58
  export async function detectAndChooseServer() {
43
59
  console.log(chalk.gray(' Detecting best server...'));
44
- const probes = await probeAll();
45
- const reachable = probes.filter((p) => p.latencyMs !== null);
46
- if (reachable.length === 0) {
60
+ const tzRegion = inferRegionByTimezone();
61
+ if (tzRegion === 'cn') {
62
+ const reachable = await verifyReachable(SERVERS.cn.url);
63
+ if (reachable) {
64
+ console.log(chalk.gray(` Using ${SERVERS.cn.label} (timezone: China)`));
65
+ return SERVERS.cn.url;
66
+ }
67
+ const fallback = await verifyReachable(SERVERS.global.url);
68
+ if (fallback) {
69
+ console.log(chalk.gray(` Using ${SERVERS.global.label} (cn unreachable, fallback)`));
70
+ return SERVERS.global.url;
71
+ }
47
72
  console.error(chalk.red(' Error: cannot reach any server.'));
48
73
  process.exit(1);
49
74
  }
50
- const best = reachable[0];
51
- console.log(chalk.gray(` Using ${best.label} (${best.latencyMs}ms)`));
52
- return best.url;
75
+ // Non-CN timezone: check IP to catch servers in China with UTC timezone
76
+ const ipRegion = await detectRegionByIP();
77
+ const region = ipRegion ?? tzRegion;
78
+ const reachable = await verifyReachable(SERVERS[region].url);
79
+ if (reachable) {
80
+ const source = ipRegion ? 'IP geolocation' : 'timezone';
81
+ console.log(chalk.gray(` Using ${SERVERS[region].label} (${source})`));
82
+ return SERVERS[region].url;
83
+ }
84
+ // Fallback to the other server
85
+ const other = region === 'cn' ? 'global' : 'cn';
86
+ const fallback = await verifyReachable(SERVERS[other].url);
87
+ if (fallback) {
88
+ console.log(chalk.gray(` Using ${SERVERS[other].label} (fallback)`));
89
+ return SERVERS[other].url;
90
+ }
91
+ console.error(chalk.red(' Error: cannot reach any server.'));
92
+ process.exit(1);
53
93
  }
54
94
  export function urlToRegion(url) {
55
95
  return url.includes('shennian.net') ? 'cn' : 'global';
@@ -1 +1 @@
1
- {"version":3,"file":"region.js","sourceRoot":"","sources":["../../src/region.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,MAAM,CAAC,MAAM,OAAO,GAAmD;IACrE,EAAE,EAAM,EAAE,GAAG,EAAE,sBAAsB,EAAE,KAAK,EAAE,uBAAuB,EAAE;IACvE,MAAM,EAAE,EAAE,GAAG,EAAE,qBAAqB,EAAG,KAAK,EAAE,sBAAsB,EAAE;CACvE,CAAA;AAED,MAAM,OAAO,GAAa,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;AAE1C,KAAK,UAAU,cAAc,CAAC,GAAW,EAAE,SAAS,GAAG,IAAI;IACzD,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAA;IACxC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,SAAS,CAAC,CAAA;IAC7D,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAA;IAC/B,IAAI,CAAC;QACH,MAAM,KAAK,CAAC,GAAG,GAAG,SAAS,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC,CAAA;QAC3D,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,CAAA;IAC9C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,KAAK,CAAC,CAAA;IACrB,CAAC;AACH,CAAC;AAID,KAAK,UAAU,QAAQ;IACrB,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAC/B,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,EAAwB,EAAE;QACjD,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;QACtC,MAAM,SAAS,GAAG,MAAM,cAAc,CAAC,GAAG,CAAC,CAAA;QAC3C,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,CAAA;IAC1C,CAAC,CAAC,CACH,CAAA;IACD,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QAC3B,IAAI,CAAC,CAAC,SAAS,KAAK,IAAI,IAAI,CAAC,CAAC,SAAS,KAAK,IAAI;YAAE,OAAO,CAAC,CAAA;QAC1D,IAAI,CAAC,CAAC,SAAS,KAAK,IAAI;YAAE,OAAO,CAAC,CAAA;QAClC,IAAI,CAAC,CAAC,SAAS,KAAK,IAAI;YAAE,OAAO,CAAC,CAAC,CAAA;QACnC,OAAO,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,CAAA;IAClC,CAAC,CAAC,CAAA;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB;IACzC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC,CAAA;IACrD,MAAM,MAAM,GAAG,MAAM,QAAQ,EAAE,CAAA;IAE/B,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,IAAI,CAAC,CAAA;IAC5D,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAC,CAAA;QAC7D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,CAAA;IACzB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,SAAS,KAAK,CAAC,CAAC,CAAA;IACtE,OAAO,IAAI,CAAC,GAAG,CAAA;AACjB,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,OAAO,GAAG,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAA;AACvD,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,MAAc;IACxC,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAA;AAC5B,CAAC"}
1
+ {"version":3,"file":"region.js","sourceRoot":"","sources":["../../src/region.ts"],"names":[],"mappings":"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"}