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 +1 -1
- package/dist/server/index.js +1 -1
- package/package.json +1 -1
- package/src/admin/service/index.ts +118 -83
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.
|
|
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",
|
package/dist/server/index.js
CHANGED
|
@@ -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.
|
|
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
|
@@ -3,7 +3,13 @@ import { BaseService } from './base'
|
|
|
3
3
|
|
|
4
4
|
const SIDE_ID = 'admin'
|
|
5
5
|
|
|
6
|
-
/**
|
|
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
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
159
|
-
|
|
160
|
-
const
|
|
161
|
-
|
|
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
|
|
169
|
-
if (
|
|
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
|
-
|
|
178
|
-
|
|
179
|
-
const base = new BaseService(
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
|
219
|
-
return
|
|
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
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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
|
-
|
|
267
|
-
|
|
268
|
-
|
|
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
|
|
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
|
-
|
|
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 */
|