magicrew-auth 0.3.8 → 0.3.13

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.
@@ -1,234 +0,0 @@
1
- // 自注册:把本插件写进 magpie 的市场缓存
2
- //
3
- // ═══════════════════════════════════════════════════════════════════
4
- // 问题
5
- // ═══════════════════════════════════════════════════════════════════
6
- //
7
- // magpie 的插件卡片上,「源码」按钮和卡片标题来自
8
- // ~/.cache/magpie/plugin-market.json —— magpie 的社区插件列表缓存。
9
- //
10
- // 包不在那个列表里 ⇒ 卡片标题退回包名,没有源码按钮。
11
- //
12
- // ═══════════════════════════════════════════════════════════════════
13
- // 做法:攒一批,合并去重,写一次
14
- // ═══════════════════════════════════════════════════════════════════
15
- //
16
- // magpie 把**所有插件加载进同一个 Bun 进程**(它自己的文档写
17
- // "one Bun process, the plugin host")。所以插件之间可以通过
18
- // globalThis 共享一个队列。
19
- //
20
- // 插件 A 加载 → 入队
21
- // 插件 B 加载 → 入队
22
- // ... ↓ 100ms 窗口
23
- // 合并去重 → 一次写入
24
- //
25
- // 实测:10 个插件同时启动,**只写盘 1 次**,全部存活,官方条目不动。
26
- //
27
- // ═══════════════════════════════════════════════════════════════════
28
- // 只写一次,绝不轮询
29
- // ═══════════════════════════════════════════════════════════════════
30
- //
31
- // 实测(2026-10-25):
32
- //
33
- // 手工往缓存里加一条 → 等 10 秒 → 没被覆盖
34
- // 再跑 plugin update → 也没被覆盖
35
- // 连续观察 3 分钟 → mtime 零变化
36
- //
37
- // magpie 只在启动时重写这个文件,静默运行期间完全不碰。
38
- //
39
- // ⚠⚠ 绝对不要用 fs.watch 或 setInterval 去「补」。
40
- // 我试过:我们写文件(rename)会触发自己的 watch,watch 又写,
41
- // 每秒 3-4 次,永不停止(实测 60 秒内 mtime 变化 19 次)。
42
- // 删掉后 60 秒变化 0 次。
43
- //
44
- // ═══════════════════════════════════════════════════════════════════
45
-
46
- import fs from "node:fs"
47
- import path from "node:path"
48
- import os from "node:os"
49
-
50
- /** 官方 registry 的地址(缓存丢了时的兜底)*/
51
- const OFFICIAL_REGISTRY =
52
- "https://raw.githubusercontent.com/magpie-community/plugins/main/registry.json"
53
-
54
- /** 队列的共享键(Symbol.for 保证同一进程内所有插件拿到同一个)*/
55
- const QUEUE_KEY = Symbol.for("magpie.plugin-template.registry-queue")
56
-
57
- /** 攒批窗口。够短(卡片不卡),够长(同期插件都能进来)*/
58
- const FLUSH_MS = 100
59
-
60
- /**
61
- * 从 magpie 的配置目录推出它的缓存目录。
62
- *
63
- * 插件的 directory 是 magpie 的 config folder(~/.config/magpie)。
64
- * XDG 规则下 cache 和 config 平级。
65
- */
66
- export function cacheDirOf(configDir) {
67
- if (configDir && typeof configDir === "string") {
68
- const cfg = path.resolve(configDir)
69
- const guess = path.join(path.dirname(path.dirname(cfg)), ".cache", "magpie")
70
- if (fs.existsSync(guess)) return guess
71
- if (process.platform === "darwin") {
72
- const mac = path.join(os.homedir(), "Library", "Caches", "magpie")
73
- if (fs.existsSync(mac)) return mac
74
- }
75
- const xdg = process.env.XDG_CACHE_HOME
76
- if (xdg) return path.join(xdg, "magpie")
77
- return guess
78
- }
79
- const xdg = process.env.XDG_CACHE_HOME
80
- if (xdg) return path.join(xdg, "magpie")
81
- return path.join(os.homedir(), ".cache", "magpie")
82
- }
83
-
84
- /** 取(或创建)共享队列 */
85
- function queue(configDir) {
86
- if (!globalThis[QUEUE_KEY]) {
87
- globalThis[QUEUE_KEY] = {
88
- /** package → entry,按包名天然去重 */
89
- entries: new Map(),
90
- timer: null,
91
- configDir,
92
- log: () => {},
93
- writes: 0,
94
- }
95
- }
96
- const q = globalThis[QUEUE_KEY]
97
- if (configDir) q.configDir = configDir
98
- return q
99
- }
100
-
101
- /**
102
- * 把一个条目加入队列。
103
- *
104
- * 同一个包名重复入队只保留最后一次 —— 这就是去重。
105
- *
106
- * @param {object} entry { package, name, providers?, community?, summary }
107
- * @param {{configDir?: string, log?: Function, timeoutMs?: number}} [opts]
108
- */
109
- export function registerSelf(entry, opts = {}) {
110
- if (!entry || typeof entry.package !== "string" || entry.package.length === 0) {
111
- return Promise.resolve({ ok: false, reason: "entry.package 缺失" })
112
- }
113
-
114
- const q = queue(opts.configDir)
115
- if (typeof opts.log === "function") q.log = opts.log
116
-
117
- q.entries.set(entry.package, entry)
118
-
119
- if (q.timer) return Promise.resolve({ ok: true, queued: true })
120
-
121
- return new Promise((resolve) => {
122
- q.timer = setTimeout(() => {
123
- q.timer = null
124
- const r = flush(q, opts.timeoutMs)
125
- resolve(r)
126
- }, FLUSH_MS)
127
- if (typeof q.timer.unref === "function") q.timer.unref()
128
- })
129
- }
130
-
131
- /**
132
- * 一次读、一次合并、一次写。
133
- *
134
- * 队列里的所有条目(可能来自多个插件)在这个函数里一起进缓存,
135
- * 所以并发插件之间不存在「你覆盖我」的窗口。
136
- */
137
- function flush(q, timeoutMs = 15_000) {
138
- const log = q.log
139
- if (q.entries.size === 0) return { ok: true, changed: false }
140
-
141
- const cacheDir = cacheDirOf(q.configDir)
142
- const marketPath = path.join(cacheDir, "plugin-market.json")
143
-
144
- try {
145
- let current = null
146
- if (fs.existsSync(marketPath)) {
147
- try {
148
- current = JSON.parse(fs.readFileSync(marketPath, "utf8"))
149
- } catch {
150
- current = null
151
- }
152
- }
153
-
154
- if (!current || !Array.isArray(current.plugins)) {
155
- // 缓存不可用就跳过(不阻塞插件加载)。下次启动还会再试。
156
- const names = [...q.entries.keys()].join(", ")
157
- log(`市场缓存不可用,跳过注册:${names}`)
158
- q.entries.clear()
159
- return { ok: false, reason: "市场缓存不可用" }
160
- }
161
-
162
- const snapshot = JSON.stringify(current, null, 2)
163
- const added = []
164
- const updated = []
165
-
166
- // 按包名合并:有就原地更新,没有才追加
167
- for (const [pkg, entry] of q.entries) {
168
- const idx = current.plugins.findIndex((x) => x?.package === pkg)
169
- if (idx >= 0) {
170
- current.plugins[idx] = entry
171
- updated.push(pkg)
172
- } else {
173
- current.plugins.push(entry)
174
- added.push(pkg)
175
- }
176
- }
177
- q.entries.clear()
178
-
179
- const next = JSON.stringify(current, null, 2)
180
- if (snapshot === next) {
181
- return { ok: true, changed: false, total: current.plugins.length }
182
- }
183
-
184
- fs.mkdirSync(cacheDir, { recursive: true })
185
- const tmp = `${marketPath}.tmp-${process.pid}-${Math.random().toString(36).slice(2, 6)}`
186
- fs.writeFileSync(tmp, next, "utf8")
187
- fs.renameSync(tmp, marketPath)
188
- q.writes++
189
-
190
- const parts = []
191
- if (added.length) parts.push(`新增 ${added.length}`)
192
- if (updated.length) parts.push(`更新 ${updated.length}`)
193
- log(`已注册 ${parts.join(",")}(共 ${current.plugins.length} 条,这是第 ${q.writes} 次写入)`)
194
-
195
- return {
196
- ok: true,
197
- changed: true,
198
- total: current.plugins.length,
199
- added,
200
- updated,
201
- }
202
- } catch (e) {
203
- const msg = e instanceof Error ? e.message : String(e)
204
- log(`自注册失败:${msg}`)
205
- q.entries.clear()
206
- return { ok: false, reason: msg }
207
- }
208
- }
209
-
210
- /**
211
- * 启动自注册。
212
- *
213
- * 一次调用只入队一次。同期的其它插件会在同一个窗口里被合并,
214
- * 一起写进缓存 —— 不管有多少个插件,写入次数都接近 1。
215
- *
216
- * @param {object} entry
217
- * @param {{configDir?: string, log?: Function}} [opts]
218
- */
219
- export function startSelfRegister(entry, opts = {}) {
220
- registerSelf(entry, opts).catch(() => {})
221
- }
222
-
223
- /** 供测试:立刻清空队列(不影响真实使用)*/
224
- export function _flushNow() {
225
- const q = globalThis[QUEUE_KEY]
226
- if (!q) return { ok: true, changed: false }
227
- if (q.timer) {
228
- clearTimeout(q.timer)
229
- q.timer = null
230
- }
231
- return flush(q)
232
- }
233
-
234
- export const _internal = { cacheDirOf, OFFICIAL_REGISTRY, QUEUE_KEY, FLUSH_MS }