daily-question 1.1.0 → 2.1.0

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/store.js CHANGED
@@ -147,18 +147,18 @@ export class Store {
147
147
  const draft = structuredClone(data)
148
148
  const result = await mutate(draft)
149
149
  draft.updatedAt = new Date().toISOString()
150
+ await this.write(draft)
150
151
  this.data = draft
151
- await this.write()
152
152
  return result
153
153
  }
154
154
  this.queue = this.queue.then(run, run)
155
155
  return this.queue
156
156
  }
157
157
 
158
- async write() {
158
+ async write(data = this.data) {
159
159
  await mkdir(this.dir, { recursive: true })
160
160
  const tmp = `${this.file}.${process.pid}.${Date.now()}.tmp`
161
- await writeFile(tmp, JSON.stringify(this.data, null, 2), 'utf8')
161
+ await writeFile(tmp, JSON.stringify(data, null, 2), 'utf8')
162
162
  await rename(tmp, this.file)
163
163
  }
164
164
  }
package/lib/subjects.js CHANGED
@@ -100,6 +100,22 @@ export const SUBJECT_CATALOG = [
100
100
 
101
101
  const CATALOG_BY_ID = new Map(SUBJECT_CATALOG.map((item) => [item.id, item]))
102
102
 
103
+ /** 参考考试独立于学习科目;不会因此在每日科目列表中新增408。 */
104
+ export const REFERENCE_EXAMS = [
105
+ ...SUBJECT_CATALOG.map(({id,name,code}) => ({id,name,code})),
106
+ {id:'cs408',name:'计算机学科专业基础',code:'408'},
107
+ ]
108
+
109
+ export function referenceExamFor(subject) {
110
+ if (!subject?.custom) return subject
111
+ if (!subject.referenceSubjectId || subject.referenceSubjectId === 'auto') return {id:'auto_'+subject.id,name:subject.name,code:subject.code || ''}
112
+ if (subject.referenceSubjectId === 'independent') {
113
+ const name = typeof subject.referenceExamName === 'string' ? subject.referenceExamName.trim() : ''
114
+ return name ? {id:'independent_'+subject.id,name,code:subject.referenceExamCode || ''} : undefined
115
+ }
116
+ return REFERENCE_EXAMS.find((exam) => exam.id === subject.referenceSubjectId)
117
+ }
118
+
103
119
  /** 全部可选科目 = 内置目录 + 用户自定义。 */
104
120
  export function allSubjects(customSubjects = []) {
105
121
  const extra = Array.isArray(customSubjects)
@@ -113,6 +129,9 @@ export function allSubjects(customSubjects = []) {
113
129
  outline: Array.isArray(item.outline) ? item.outline : [],
114
130
  guidance: typeof item.guidance === 'string' ? item.guidance : '',
115
131
  custom: true,
132
+ referenceSubjectId: typeof item.referenceSubjectId === 'string' ? item.referenceSubjectId : '',
133
+ referenceExamName: typeof item.referenceExamName === 'string' ? item.referenceExamName : '',
134
+ referenceExamCode: typeof item.referenceExamCode === 'string' ? item.referenceExamCode : '',
116
135
  }))
117
136
  : []
118
137
  return [...SUBJECT_CATALOG, ...extra]
package/lib/tutor.js CHANGED
@@ -7,7 +7,7 @@ const TUTOR_SYSTEM = `你是中国大陆硕士研究生招生考试的辅导老
7
7
  - 只讨论当前这道题:题干、选项、标准答案、解析以及由它们直接延伸的知识点。如果学生的提问明显超出这道题的范围,先说明它与本题的关系,再简短回答,不要把它当成跨题的通用聊天。
8
8
  - 用 Markdown 回答;数学公式用 LaTeX(行内 $...$,独立公式 $$...$$)。
9
9
  - 解释要一步步来,指出关键依据,不要只给结论。
10
- - 你给出的解释是对官方答案详解的补充说明,不能宣称修改了官方答案与解析。
10
+ - 你给出的解释是对当前题目参考答案的补充说明,不能宣称修改了已保存的答案与解析;模型生成、导入或修订的答案都不能称为“官方答案”。
11
11
  - 如果题目资料里标记答案尚未核验或缺少参考答案,必须如实说明,不要把未核验的内容说成确定结论。
12
12
  - 不要编造与题目无关的事实。`
13
13
 
@@ -29,12 +29,13 @@ export function describeQuestionContext(question, answerDetail) {
29
29
  for (const option of question.options) lines.push(`${option.key}. ${option.text}`)
30
30
  }
31
31
  lines.push('')
32
- if (answerDetail) {
33
- lines.push('--- 标准答案(已校验的正式版本)---')
32
+ if (answerDetail?.answer?.trim()) {
33
+ const status=answerDetail.verified===true?'已通过独立校验,仍非官方认证':'尚未独立核验,可能有误,请先核对'
34
+ lines.push(`--- 参考答案(${status})---`)
34
35
  lines.push(answerDetail.answer)
35
36
  lines.push('')
36
- lines.push('--- 解析(已校验的正式版本)---')
37
- lines.push(answerDetail.explanation)
37
+ lines.push(`--- 参考解析(${status})---`)
38
+ lines.push(answerDetail.explanation||'(尚未提供解析)')
38
39
  } else {
39
40
  lines.push('--- 答案状态 ---')
40
41
  lines.push('本题的参考答案与解析尚未通过独立校验或尚未生成,请如实说明,不要给出确定的“标准答案”。')
package/lib/updates.js ADDED
@@ -0,0 +1,49 @@
1
+ import { readFileSync } from 'node:fs'
2
+
3
+ export const PLUGIN_VERSION=JSON.parse(readFileSync(new URL('../package.json',import.meta.url),'utf8')).version
4
+ export const UPDATE_INTERVAL=6*60*60*1000
5
+ const REGISTRY='https://registry.npmjs.org/daily-question/latest'
6
+
7
+ function versionParts(value) {
8
+ if(typeof value!=='string')throw new Error('npm 返回的版本号无效')
9
+ const match=/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([\da-zA-Z-]+(?:\.[\da-zA-Z-]+)*))?(?:\+[\da-zA-Z-]+(?:\.[\da-zA-Z-]+)*)?$/.exec(value)
10
+ if(!match||match[4]?.split('.').some((p)=>/^0\d+$/.test(p)))throw new Error('npm 返回的版本号无效')
11
+ return {numbers:match.slice(1,4).map(BigInt),pre:match[4]?.split('.')}
12
+ }
13
+
14
+ export function newerVersion(latest,current) {
15
+ const a=versionParts(latest),b=versionParts(current)
16
+ for(let i=0;i<3;i++)if(a.numbers[i]!==b.numbers[i])return a.numbers[i]>b.numbers[i]
17
+ if(!a.pre||!b.pre)return Boolean(!a.pre&&b.pre)
18
+ for(let i=0;i<Math.max(a.pre.length,b.pre.length);i++) {
19
+ const x=a.pre[i],y=b.pre[i]
20
+ if(x===y)continue
21
+ if(x===undefined||y===undefined)return y===undefined
22
+ const nx=/^\d+$/.test(x),ny=/^\d+$/.test(y)
23
+ return nx&&ny?BigInt(x)>BigInt(y):nx!==ny?!nx:x>y
24
+ }
25
+ return false
26
+ }
27
+
28
+ export function createUpdateChecker({fetchImpl=globalThis.fetch,currentVersion=PLUGIN_VERSION,now=Date.now}={}) {
29
+ let cached,pending
30
+ return async ({force=false}={})=>{
31
+ if(pending)return {...await pending}
32
+ if(force!==true&&cached&&now()-cached.at>=0&&now()-cached.at<UPDATE_INTERVAL)return {...cached.result}
33
+ pending=(async()=>{
34
+ try {
35
+ const response=await fetchImpl(REGISTRY,{signal:AbortSignal.timeout(10000),redirect:'error',headers:{accept:'application/json'}})
36
+ if(!response.ok)throw new Error(`npm 服务返回 ${response.status}`)
37
+ const metadata=await response.json()
38
+ if(metadata?.name!=='daily-question')throw new Error('npm 返回的包信息不匹配')
39
+ const updateAvailable=newerVersion(metadata.version,currentVersion)
40
+ const at=now(),result={ok:true,currentVersion,latestVersion:metadata.version,updateAvailable,checkedAt:new Date(at).toISOString(),packageUrl:'https://www.npmjs.com/package/daily-question'}
41
+ cached={at,result}
42
+ return {...result}
43
+ } catch(error) {
44
+ throw new Error(`检查更新失败:${error.name==='TimeoutError'?'连接 npm 超时,请稍后重试':error.message}`)
45
+ }
46
+ })()
47
+ try{return {...await pending}}finally{pending=null}
48
+ }
49
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "daily-question",
3
- "version": "1.1.0",
3
+ "version": "2.1.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "考研每日一题 — DSH 插件:每科每天一道独立校验过的考研题,含倒计时、错题本、收藏与按题隔离的追问。",
@@ -31,7 +31,8 @@
31
31
  "node": ">=20.3.0"
32
32
  },
33
33
  "scripts": {
34
- "test": "node test/check.mjs && node test/service-check.mjs && node test/render-check.mjs"
34
+ "test": "node test/check.mjs && node test/service-check.mjs && node test/tutor-status-check.mjs && node test/attempt-binding-check.mjs && node test/list-performance-check.mjs && node test/daily-attempt-check.mjs && node test/render-check.mjs && node test/ui-race-check.mjs && node test/followup-check.mjs && node test/task-check.mjs && node test/generation-check.mjs && node test/generation-history-check.mjs && node test/reference-check.mjs && node test/reference-reader-check.mjs && node test/research-check.mjs && node test/pdf-safety-check.mjs && node test/update-check.mjs",
35
+ "test:live": "node test/live-check.mjs"
35
36
  },
36
37
  "exports": {
37
38
  ".": "./index.js",
@@ -45,6 +46,7 @@
45
46
  "cordis.patch.yml",
46
47
  "locale/*.json",
47
48
  "icon.svg",
49
+ "CHANGELOG.md",
48
50
  "lib/*.js",
49
51
  "vendor/mathjax/*"
50
52
  ],