vome-core 0.0.8 → 0.0.9

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/dist/index.js CHANGED
@@ -121,7 +121,7 @@ class BaseController {
121
121
  // package.json
122
122
  var package_default = {
123
123
  name: "vome-core",
124
- version: "0.0.8",
124
+ version: "0.0.9",
125
125
  description: "Vome framework \u2014 shared + Bun/Elysia server + Vue admin",
126
126
  license: "MIT",
127
127
  type: "module",
@@ -3083,7 +3083,7 @@ function defineAuthMacro(name, resolve) {
3083
3083
  // package.json
3084
3084
  var package_default = {
3085
3085
  name: "vome-core",
3086
- version: "0.0.8",
3086
+ version: "0.0.9",
3087
3087
  description: "Vome framework \u2014 shared + Bun/Elysia server + Vue admin",
3088
3088
  license: "MIT",
3089
3089
  type: "module",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vome-core",
3
- "version": "0.0.8",
3
+ "version": "0.0.9",
4
4
  "description": "Vome framework — shared + Bun/Elysia server + Vue admin",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -3,7 +3,13 @@ import { BaseService } from './base'
3
3
 
4
4
  const SIDE_ID = 'admin'
5
5
 
6
- /** 标准 CRUD 之外的常用接口(EPS 未就绪时也可用) */
6
+ /**
7
+ * 每次发版递增。globalThis 单例若跨 HMR/旧包残留,靠此戳强制换新根对象并重建。
8
+ * 根因:旧 singleton 只有 CRUD、无 tree/roleMap,且 plugin-info 错名导致 base.plugin 缺失。
9
+ */
10
+ const SERVICE_BUILD = 9
11
+
12
+ /** 标准 CRUD 之外的常用接口(不依赖 EPS 是否已返回) */
7
13
  const FALLBACK_EXTRA_APIS: Record<
8
14
  string,
9
15
  Array<{ method: string; path: string; perms?: string[] }>
@@ -52,7 +58,6 @@ const FALLBACK_EXTRA_APIS: Record<
52
58
  ],
53
59
  }
54
60
 
55
- /** EPS 不可用时仍保证业务页链式可调 */
56
61
  const FALLBACK_PREFIXES = [
57
62
  '/admin/base/menu',
58
63
  '/admin/base/user',
@@ -82,7 +87,6 @@ function isValidName(name: string) {
82
87
  return Boolean(name) && !['{', '}', ':'].some((c) => name.includes(c))
83
88
  }
84
89
 
85
- /** 无 EPS perms 时生成权限码 */
86
90
  function resolvePermCode(prefix: string, action: string) {
87
91
  const parts = prefix.replace(/^\//, '').split('/').filter(Boolean)
88
92
  const module = parts[1] || parts[0] || 'base'
@@ -99,10 +103,13 @@ function resolvePermCode(prefix: string, action: string) {
99
103
  return `${module}:${resource}:${action}`
100
104
  }
101
105
 
102
- function getMethodKeys(leaf: Record<string, unknown>) {
103
- return Object.keys(leaf).filter(
104
- (k) => !['namespace', 'permission', '_permission', 'request', 'search'].includes(k),
105
- )
106
+ function prefixToParts(prefix: string) {
107
+ const raw = prefix.replace(/^\//, '')
108
+ return raw
109
+ .replace(new RegExp(`^${SIDE_ID}/?`), '')
110
+ .split('/')
111
+ .filter(Boolean)
112
+ .map(toCamel)
106
113
  }
107
114
 
108
115
  function bindCrud(leaf: ServiceLeaf, base: BaseService) {
@@ -132,14 +139,6 @@ function bindApis(leaf: ServiceLeaf, base: BaseService, apis: EpsApiItem[]) {
132
139
  const name = methodName(api.path)
133
140
  if (!isValidName(name) || /[-:]/g.test(name)) continue
134
141
  const method = (api.method || 'get').toLowerCase()
135
- // 已有同名方法则保留(避免 fallback 覆盖 EPS 绑定)
136
- if (typeof (leaf as Record<string, unknown>)[name] === 'function') {
137
- if (!leaf.permission[name]) {
138
- leaf.permission[name] =
139
- api.perms?.[0] || resolvePermCode(`/${leaf.namespace}`, name)
140
- }
141
- continue
142
- }
143
142
  ;(leaf as Record<string, unknown>)[name] = function (
144
143
  this: BaseService,
145
144
  data?: unknown,
@@ -155,40 +154,46 @@ function bindApis(leaf: ServiceLeaf, base: BaseService, apis: EpsApiItem[]) {
155
154
  }
156
155
  }
157
156
 
158
- function createLeaf(namespace: string, apis: EpsApiItem[] = []): ServiceLeaf {
159
- const base = new BaseService(namespace)
160
- const leaf = base as unknown as ServiceLeaf
161
- leaf.namespace = namespace.replace(/^\//, '')
157
+ /** 原地重挂方法,保持叶子对象引用不变(避免页面 const log = service.base.log 失效) */
158
+ function remountLeaf(leaf: ServiceLeaf, namespace: string, apis: EpsApiItem[]) {
159
+ const ns = namespace.replace(/^\//, '')
160
+ const base = new BaseService(ns)
161
+ leaf.namespace = ns
162
162
  leaf.permission = {}
163
- leaf._permission = {}
163
+ leaf._permission = leaf._permission || {}
164
+
165
+ // 清掉旧方法再绑,避免残留错误实现
166
+ for (const key of Object.keys(leaf as object)) {
167
+ if (
168
+ key === 'namespace' ||
169
+ key === 'permission' ||
170
+ key === '_permission' ||
171
+ key === 'search'
172
+ ) {
173
+ continue
174
+ }
175
+ delete (leaf as Record<string, unknown>)[key]
176
+ }
164
177
 
165
178
  bindCrud(leaf, base)
166
179
  bindApis(leaf, base, apis)
167
180
 
168
- for (const name of getMethodKeys(leaf as unknown as Record<string, unknown>)) {
169
- if (!leaf.permission[name]) {
170
- leaf.permission[name] = resolvePermCode(`/${leaf.namespace}`, name)
171
- }
181
+ for (const name of Object.keys(leaf.permission)) {
182
+ if (leaf._permission[name] == null) leaf._permission[name] = false
172
183
  }
173
-
174
184
  return leaf
175
185
  }
176
186
 
177
- /** 合并到已有叶子:补方法,不抹掉已有自定义 API */
178
- function mergeLeaf(existing: ServiceLeaf, namespace: string, apis: EpsApiItem[]) {
179
- const base = new BaseService(namespace)
180
- // 确保 CRUD 在实例上(兼容旧 fallback)
181
- bindCrud(existing, base)
182
- bindApis(existing, base, apis)
183
- existing.namespace = namespace.replace(/^\//, '')
184
- existing.permission ||= {}
185
- existing._permission ||= {}
186
- for (const name of getMethodKeys(existing as unknown as Record<string, unknown>)) {
187
- if (!existing.permission[name]) {
188
- existing.permission[name] = resolvePermCode(`/${existing.namespace}`, name)
189
- }
190
- }
191
- return existing
187
+ function createLeaf(namespace: string, apis: EpsApiItem[] = []): ServiceLeaf {
188
+ const ns = namespace.replace(/^\//, '')
189
+ const base = new BaseService(ns)
190
+ const leaf = base as unknown as ServiceLeaf
191
+ leaf.namespace = ns
192
+ leaf.permission = {}
193
+ leaf._permission = {}
194
+ bindCrud(leaf, base)
195
+ bindApis(leaf, base, apis)
196
+ return leaf
192
197
  }
193
198
 
194
199
  function attachLeaf(
@@ -197,12 +202,7 @@ function attachLeaf(
197
202
  apis: EpsApiItem[] = [],
198
203
  ) {
199
204
  const raw = prefix.replace(/^\//, '')
200
- const arr = raw
201
- .replace(new RegExp(`^${SIDE_ID}/?`), '')
202
- .split('/')
203
- .filter(Boolean)
204
- .map(toCamel)
205
-
205
+ const arr = prefixToParts(prefix)
206
206
  if (!arr.length) return undefined
207
207
 
208
208
  let node: Record<string, unknown> = tree as unknown as Record<string, unknown>
@@ -210,13 +210,18 @@ function attachLeaf(
210
210
  const key = arr[i]!
211
211
  const isLast = i === arr.length - 1
212
212
  if (!isLast) {
213
- if (!node[key] || typeof node[key] !== 'object') node[key] = {}
213
+ const cur = node[key] as ServiceLeaf | Record<string, unknown> | undefined
214
+ // 中间节点若被误建成叶子,换成容器(否则 dict/info 挂不上)
215
+ if (!cur || typeof cur !== 'object' || 'namespace' in cur) {
216
+ node[key] = {}
217
+ }
214
218
  node = node[key] as Record<string, unknown>
215
219
  continue
216
220
  }
221
+
217
222
  const existing = node[key] as ServiceLeaf | undefined
218
- if (existing?.namespace) {
219
- return mergeLeaf(existing, raw, apis)
223
+ if (existing && typeof existing === 'object' && existing.namespace) {
224
+ return remountLeaf(existing, raw, apis)
220
225
  }
221
226
  const leaf = createLeaf(raw, apis)
222
227
  node[key] = leaf
@@ -245,27 +250,62 @@ function buildFromEps(map: EpsModuleMap) {
245
250
  }
246
251
  }
247
252
 
253
+ function extraApisOf(prefix: string): EpsApiItem[] {
254
+ return (FALLBACK_EXTRA_APIS[prefix] || []).map(
255
+ (a) =>
256
+ ({
257
+ method: a.method,
258
+ path: a.path,
259
+ perms: a.perms,
260
+ }) as EpsApiItem,
261
+ )
262
+ }
263
+
264
+ /**
265
+ * 用 fallback 补全:已有叶子则合并补方法(不整棵删,避免打断引用)。
266
+ * 对 fallback 前缀:若 EPS 已挂过,只补缺失的 extra;attachLeaf/remount 会重绑整叶。
267
+ */
248
268
  function buildFallback() {
249
269
  for (const prefix of FALLBACK_PREFIXES) {
250
- const extra = FALLBACK_EXTRA_APIS[prefix] || []
251
- const apis = extra.map(
252
- (a) =>
253
- ({
254
- method: a.method,
255
- path: a.path,
256
- perms: a.perms,
257
- }) as EpsApiItem,
258
- )
259
- attachLeaf(service, prefix, apis)
270
+ const parts = prefixToParts(prefix)
271
+ let node: Record<string, unknown> = service as unknown as Record<string, unknown>
272
+ let missing = false
273
+ for (let i = 0; i < parts.length; i++) {
274
+ const key = parts[i]!
275
+ if (!node[key]) {
276
+ missing = true
277
+ break
278
+ }
279
+ if (i < parts.length - 1) {
280
+ node = node[key] as Record<string, unknown>
281
+ }
282
+ }
283
+ const leaf = missing
284
+ ? undefined
285
+ : (node[parts[parts.length - 1]!] as ServiceLeaf | undefined)
286
+
287
+ if (leaf?.namespace) {
288
+ // 只补 extra 里还没有的方法,保留 EPS 已绑方法
289
+ const extras = extraApisOf(prefix).filter((api) => {
290
+ const name = methodName(api.path)
291
+ return typeof (leaf as Record<string, unknown>)[name] !== 'function'
292
+ })
293
+ if (extras.length) {
294
+ const base = new BaseService(leaf.namespace)
295
+ bindApis(leaf, base, extras)
296
+ }
297
+ continue
298
+ }
299
+ attachLeaf(service, prefix, extraApisOf(prefix))
260
300
  }
261
301
  }
262
302
 
263
303
  type ServiceRoot = Eps.Service & { request: BaseService['request'] }
264
304
 
265
- /**
266
- * 全局单例:npm 包经多路径解析时避免出现两份 service(一份有 tree、一份没有)。
267
- */
268
- const g = globalThis as typeof globalThis & { __vome_admin_service__?: ServiceRoot }
305
+ const g = globalThis as typeof globalThis & {
306
+ __vome_admin_service__?: ServiceRoot
307
+ __vome_admin_service_build__?: number
308
+ }
269
309
 
270
310
  function createServiceRoot(): ServiceRoot {
271
311
  const root = new BaseService()
@@ -274,14 +314,23 @@ function createServiceRoot(): ServiceRoot {
274
314
  } as ServiceRoot
275
315
  }
276
316
 
317
+ function getOrCreateRoot(): ServiceRoot {
318
+ if (
319
+ !g.__vome_admin_service__ ||
320
+ g.__vome_admin_service_build__ !== SERVICE_BUILD
321
+ ) {
322
+ g.__vome_admin_service__ = createServiceRoot()
323
+ g.__vome_admin_service_build__ = SERVICE_BUILD
324
+ }
325
+ return g.__vome_admin_service__
326
+ }
327
+
277
328
  /** 全局链式 service(service.base.user.page()) */
278
- export const service: ServiceRoot =
279
- g.__vome_admin_service__ ?? (g.__vome_admin_service__ = createServiceRoot())
329
+ export const service: ServiceRoot = getOrCreateRoot()
280
330
 
281
- // 同步兜底(含 tree / menus 等),页面 import 时即可调用
282
331
  buildFallback()
283
332
 
284
- /** 从 EPS 构建/刷新 service 树;失败时用兜底 prefix + 自定义 API */
333
+ /** 从 EPS 构建/刷新;再 fallback 补自定义方法 */
285
334
  export async function createEps(force = false) {
286
335
  try {
287
336
  const map = (await loadEps(force)) || {}
@@ -301,7 +350,6 @@ if (import.meta.hot) {
301
350
  })
302
351
  }
303
352
 
304
- /** 登录后写入 _permission,供 CRUD / 按钮显隐 */
305
353
  export function setServicePerms(perms: string[], isSuper = false) {
306
354
  const list = perms || []
307
355
 
@@ -335,7 +383,6 @@ export function setServicePerms(perms: string[], isSuper = false) {
335
383
  deep(service)
336
384
  }
337
385
 
338
- /** 解析 service.base.user 或 'base.user' */
339
386
  export function resolveService(path: string): ServiceLeaf | undefined {
340
387
  const parts = path.replace(/^\//, '').split('.').filter(Boolean)
341
388
  let cur: unknown = service
@@ -349,24 +396,12 @@ export function resolveService(path: string): ServiceLeaf | undefined {
349
396
  return undefined
350
397
  }
351
398
 
352
- /** 按 prefix 取或创建 service 叶子 */
353
399
  export function serviceOf(prefix: string): ServiceLeaf {
354
400
  const raw = prefix.replace(/\/$/, '')
355
- const path = raw
356
- .replace(/^\//, '')
357
- .replace(new RegExp(`^${SIDE_ID}/?`), '')
358
- .split('/')
359
- .filter(Boolean)
360
- .map(toCamel)
361
- .join('.')
401
+ const path = prefixToParts(raw).join('.')
362
402
  const hit = resolveService(path)
363
403
  if (hit) return hit
364
- const extra = FALLBACK_EXTRA_APIS[raw.startsWith('/') ? raw : `/${raw}`] || []
365
- return attachLeaf(
366
- service,
367
- raw,
368
- extra.map((a) => ({ method: a.method, path: a.path, perms: a.perms }) as EpsApiItem),
369
- ) as ServiceLeaf
404
+ return attachLeaf(service, raw, extraApisOf(raw.startsWith('/') ? raw : `/${raw}`)) as ServiceLeaf
370
405
  }
371
406
 
372
407
  /** @deprecated 请用 service.base.xxx */