@yongdall/core 0.4.3 → 0.5.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/index.mjs CHANGED
@@ -1,6 +1,7 @@
1
- import { Hook, Search, isFunction } from "@yongdall/common";
1
+ import { Hook, LazyPromise, Search, isFunction } from "@yongdall/common";
2
2
  import { Query, Where, afterCreate, afterCreateMany, afterDelete, afterUpdate, beforeCreate, beforeCreateMany, beforeDelete, beforeUpdate, coefficient, decrement, expandModel, getDefinePermission, increment, multiply } from "@yongdall/model";
3
3
  import { createKeyStore, createStoreSerializable } from "@yongdall/context";
4
+ import { loadPluginProfiles } from "@yongdall/plugins";
4
5
 
5
6
  //#region packages/core/polyfill.mjs
6
7
  if (!Symbol.metadata) Symbol.metadata = Symbol.for("Symbol.metadata");
@@ -60,23 +61,21 @@ function getMenus(type, group = "") {
60
61
  //#region packages/core/models.mjs
61
62
  /** @import { Permission } from '@yongdall/types' */
62
63
  /** @import { FieldDefine, Fields, ModelTable, TableDefine } from '@yongdall/model' */
63
- /** @import { Hooks } from './types.mjs' */
64
- /** @type {Hooks['models']} */
65
- let models = Object.create(null);
66
- /** @type {Hooks['modelLoaders']} */
67
- let modelLoaders = Object.create(null);
68
- /** @type {((modelId: string, groupId?: string | null) => PromiseLike<Permission[] | null> | Permission[] | null)[]} */
69
- let getModelPermissionsHooks = [];
70
- /** @type {WeakMap<ModelTable, ModelTable>} */
71
- let modelMap = /* @__PURE__ */ new WeakMap();
72
- /** @type {Record<string, Fields<TableDefine>>} */
73
- let modelExpands = {};
74
- hooks.listen(() => {
75
- modelMap = /* @__PURE__ */ new WeakMap();
76
- models = hooks.get("models").named();
77
- modelLoaders = hooks.get("modelLoaders").named();
78
- getModelPermissionsHooks = [...hooks.get("getModelPermissions").values()].filter((v) => typeof v === "function");
79
- const modelFields = [...hooks.get("modelExpands").entriesObject()].flatMap(([p, modelId, fields]) => [fields].flatMap((v) => {
64
+ /** @import { ModelProfile } from './types.mjs' */
65
+ const ModelProfilePromise = LazyPromise.try(async function() {
66
+ return await Array.fromAsync(loadPluginProfiles("model", (v) => [v]));
67
+ });
68
+ const modelsPromise = ModelProfilePromise.lazyThen((list) => {
69
+ return Object.fromEntries(list.flatMap((v) => Object.entries(v.models || {})));
70
+ });
71
+ const modelLoadersPromise = ModelProfilePromise.lazyThen((list) => {
72
+ return Object.fromEntries(list.flatMap((v) => Object.entries(v.modelLoaders || {})));
73
+ });
74
+ const getModelPermissionsHooksPromise = ModelProfilePromise.lazyThen((list) => {
75
+ return list.map((v) => v.getModelPermissions).filter((v) => typeof v === "function");
76
+ });
77
+ const modelExpandsPromise = ModelProfilePromise.lazyThen((list) => {
78
+ const modelFields = list.flatMap((v) => Object.entries(v.modelExpands || {})).flatMap(([modelId, fields]) => [fields].flatMap((v) => {
80
79
  if (!v || typeof v !== "object" || Array.isArray(v)) return [];
81
80
  return Object.entries(v).map(([fieldName, field]) => [
82
81
  modelId,
@@ -91,8 +90,9 @@ hooks.listen(() => {
91
90
  if (!fields?.length) continue;
92
91
  expands[modelId] = Object.fromEntries(fields.map(([, fieldName, field]) => [fieldName, field]));
93
92
  }
94
- modelExpands = expands;
95
- }, -1e6);
93
+ return expands;
94
+ });
95
+ const modelMap = /* @__PURE__ */ new WeakMap();
96
96
  /**
97
97
  *
98
98
  * @param {string | string[]} ident
@@ -101,9 +101,11 @@ hooks.listen(() => {
101
101
  async function findModel(ident) {
102
102
  const [model, ...collection] = (Array.isArray(ident) ? ident : ident.split(":")).filter(Boolean);
103
103
  if (!collection.length) {
104
+ const models = await modelsPromise;
104
105
  const define = Object.hasOwn(models, model) && models[model];
105
106
  return (typeof define === "object" || typeof define === "function") && define || null;
106
107
  }
108
+ const modelLoaders = await modelLoadersPromise;
107
109
  const loader = Object.hasOwn(modelLoaders, model) && modelLoaders[model];
108
110
  if (typeof loader !== "function") return null;
109
111
  const define = await loader(collection);
@@ -120,13 +122,14 @@ async function getModel(ident) {
120
122
  const define = modelMap.get(model);
121
123
  if (define) return define;
122
124
  const modelIdent = Array.isArray(ident) ? ident.join(":") : ident;
125
+ const modelExpands = await modelExpandsPromise;
123
126
  const newDefine = expandModel(model, Object.hasOwn(modelExpands, modelIdent) ? modelExpands[modelIdent] : {});
124
127
  modelMap.set(model, newDefine);
125
128
  return newDefine;
126
129
  }
127
130
  /** @type {(modelId: string, groupId?: string?) => Promise<Permission[]?>} */
128
131
  const getModelPermissions = createKeyStore(async (modelId, groupId) => {
129
- for (const fn of getModelPermissionsHooks) {
132
+ for (const fn of await getModelPermissionsHooksPromise) {
130
133
  const permissions = await fn(modelId, groupId);
131
134
  if (permissions) return permissions;
132
135
  }
@@ -180,7 +183,7 @@ async function getCurrentTenant() {
180
183
  }
181
184
  throw new Error("[noTenant] 找不到租户");
182
185
  }
183
- async function get$1() {
186
+ async function get$2() {
184
187
  let state = tenantGetter();
185
188
  if (!state) {
186
189
  state = getCurrentTenant();
@@ -193,7 +196,7 @@ async function get$1() {
193
196
  * @returns {Promise<Tenant>}
194
197
  */
195
198
  function useTenant() {
196
- return get$1();
199
+ return get$2();
197
200
  }
198
201
 
199
202
  //#endregion
@@ -201,7 +204,7 @@ function useTenant() {
201
204
  /** @import { Permissions, Tenant } from '@yongdall/types' */
202
205
  /** @import { UserManager, MaybePromise } from './types.mjs' */
203
206
  /**
204
- * @typedef {[result: string | null, UserManager['set'] | null, UserManager['exit'] | null, user: string | null, sUser: string | null]} State
207
+ * @typedef {[result: string | null, UserManager['set'] | null, UserManager['exit'] | null, user: string | null, sUser: string | null]} UserState
205
208
  */
206
209
  const [userGetter, userSetter] = createStoreSerializable("userStatus", async (state) => {
207
210
  if (!state) return null;
@@ -221,80 +224,70 @@ const [userGetter, userSetter] = createStoreSerializable("userStatus", async (st
221
224
  sUserId
222
225
  ]);
223
226
  }, null);
224
- function getUserLoaders() {
225
- return [...hooks.get("userManager").entries()].map(([k, v]) => [k, v?.loadPlugin]).filter(([, v]) => typeof v === "function");
226
- }
227
- function getUserManagers() {
228
- /** @type {Partial<UserManager>[]} */
229
- const userMangers = [...hooks.get("userManager").values()].filter(Boolean);
230
- const defaultLogin = userMangers.find((v) => typeof v.login === "function")?.login || null;
227
+ const userManagerMap = LazyPromise.try(async function() {
228
+ return await Array.fromAsync(loadPluginProfiles("user"));
229
+ });
230
+ const userManagers = userManagerMap.lazyThen((list) => list.map((v) => v[1]));
231
+ const defaultLogin$1 = userManagers.lazyThen((userMangers) => {
232
+ return userMangers.find((v) => typeof v.login === "function")?.login || null;
233
+ });
234
+ const switchableListPromise = userManagers.lazyThen((userMangers) => {
231
235
  const switchableList = userMangers.map((v) => v.switchable).filter(isFunction);
232
- /** @type {(() => MaybePromise<string?>)[]} */
233
- const suGets = userMangers.map((v) => v.suGet).filter(isFunction);
234
- /** @type {[UserManager['get'], UserManager['set'] | null, UserManager['exit'] | null][]} */
235
- const getLoggedUserFn = [];
236
- /** @type {Record<string, ((userId: string, password: string) => MaybePromise<number>)[]>} */
237
- const verifiers = Object.create(null);
238
- /** @type {Record<string, ((userId: string, password: string?, deadline?: Date?) => MaybePromise<boolean>)[]>} */
239
- const setVerifiers = Object.create(null);
240
- const finders = userMangers.map((v) => v.find).filter((v) => typeof v === "function");
241
- const loaders = userMangers.map((v) => v.load).filter((v) => typeof v === "function");
242
- const existTesters = userMangers.map((v) => v.exist).filter((v) => typeof v === "function");
243
- const getPermissions = userMangers.map((v) => v.getPermissions).filter((v) => typeof v === "function");
244
- const getOrganizationPermissions = userMangers.map((v) => v.getOrganizationPermissions).filter((v) => typeof v === "function");
245
- const getOrganizationAllPermissions = userMangers.map((v) => v.getOrganizationAllPermissions).filter((v) => typeof v === "function");
246
- for (const { get, set, exit, find, verify, setVerify } of userMangers) {
247
- if (typeof get === "function") getLoggedUserFn.push([
248
- get,
249
- set || null,
250
- exit || null
251
- ]);
252
- if (verify) for (const [k, v] of Object.entries(verify)) {
253
- const list = verifiers[k];
254
- if (list) list.push(v);
255
- else verifiers[k] = [v];
256
- }
257
- if (setVerify) for (const [k, v] of Object.entries(setVerify)) {
258
- const list = setVerifiers[k];
259
- if (list) list.push(v);
260
- else setVerifiers[k] = [v];
261
- }
262
- }
263
236
  /**
264
237
  * @param {Tenant} tenant
265
238
  * @param {string} user
266
239
  * @param {string} sUser
267
240
  * @returns {Promise<boolean>}
268
241
  */
269
- async function switchable(tenant, user, sUser) {
242
+ return async function switchable(tenant, user, sUser) {
270
243
  if (!switchableList.length) return false;
271
244
  for (const s of switchableList) {
272
245
  if (await s(user, sUser)) continue;
273
246
  return false;
274
247
  }
275
248
  return true;
276
- }
277
- return {
278
- defaultLogin,
279
- switchable,
280
- suGets,
281
- getLoggedUserFn,
282
- finders,
283
- verifiers,
284
- setVerifiers,
285
- loaders,
286
- existTesters,
287
- getPermissions,
288
- getOrganizationPermissions,
289
- getOrganizationAllPermissions
290
249
  };
291
- }
292
- let userPluginLoaders = getUserLoaders();
293
- let { defaultLogin, switchable, suGets, getLoggedUserFn, finders, verifiers, setVerifiers, loaders, existTesters, getPermissions, getOrganizationPermissions, getOrganizationAllPermissions } = getUserManagers();
294
- hooks.listen(() => {
295
- userPluginLoaders = getUserLoaders();
296
- ({defaultLogin, switchable, suGets, getLoggedUserFn, finders, verifiers, setVerifiers, loaders, existTesters, getPermissions, getOrganizationPermissions, getOrganizationAllPermissions} = getUserManagers());
297
- }, -1e6);
250
+ });
251
+ const suGets = userManagers.lazyThen((userMangers) => {
252
+ /** @type {(() => MaybePromise<string?>)[]} */
253
+ return userMangers.map((v) => v.suGet).filter(isFunction);
254
+ });
255
+ const finders$1 = userManagers.lazyThen((userMangers) => {
256
+ return userMangers.map((v) => v.find).filter((v) => typeof v === "function");
257
+ });
258
+ const loaders = userManagers.lazyThen((userMangers) => {
259
+ return userMangers.map((v) => v.load).filter((v) => typeof v === "function");
260
+ });
261
+ const existTesters$1 = userManagers.lazyThen((userMangers) => {
262
+ return userMangers.map((v) => v.exist).filter((v) => typeof v === "function");
263
+ });
264
+ const getPermissions = userManagers.lazyThen((userMangers) => {
265
+ return userMangers.map((v) => v.getPermissions).filter((v) => typeof v === "function");
266
+ });
267
+ const getOrganizationPermissions = userManagers.lazyThen((userMangers) => {
268
+ return userMangers.map((v) => v.getOrganizationPermissions).filter((v) => typeof v === "function");
269
+ });
270
+ const getOrganizationAllPermissions = userManagers.lazyThen((userMangers) => {
271
+ return userMangers.map((v) => v.getOrganizationAllPermissions).filter((v) => typeof v === "function");
272
+ });
273
+ const getLoggedUserFn = userManagers.lazyThen((userMangers) => {
274
+ /** @type {[UserManager['get'], UserManager['set'] | null, UserManager['exit'] | null][]} */
275
+ const getLoggedUserFn = [];
276
+ for (const { get, set, exit } of userMangers) if (typeof get === "function") getLoggedUserFn.push([
277
+ get,
278
+ set || null,
279
+ exit || null
280
+ ]);
281
+ return getLoggedUserFn;
282
+ });
283
+ const verifiersPromise = userManagers.lazyThen((userMangers) => {
284
+ const list = userMangers.map((v) => v).flatMap((v) => Object.entries(v.verify || {}));
285
+ return Object.fromEntries(Object.entries(Object.groupBy(list, (v) => v[0])).map(([k, v]) => [k, v?.map((v) => v[1]) || []]));
286
+ });
287
+ const setVerifiersPromise = userManagers.lazyThen((userMangers) => {
288
+ const list = userMangers.map((v) => v).flatMap((v) => Object.entries(v.setVerify || {}));
289
+ return Object.fromEntries(Object.entries(Object.groupBy(list, (v) => v[0])).map(([k, v]) => [k, v?.map((v) => v[1]) || []]));
290
+ });
298
291
  /**
299
292
  *
300
293
  * @param {string} userId
@@ -302,17 +295,17 @@ hooks.listen(() => {
302
295
  * @returns {Promise<boolean>}
303
296
  */
304
297
  async function existUser(userId, login) {
305
- for (const fn of existTesters) if (await fn(userId, login)) return true;
298
+ for (const fn of await existTesters$1) if (await fn(userId, login)) return true;
306
299
  return false;
307
300
  }
308
301
  /**
309
302
  *
310
- * @returns {Promise<State>}
303
+ * @returns {Promise<UserState>}
311
304
  */
312
305
  async function getLoggedUser() {
313
306
  const tenant = await useTenant();
314
307
  if (tenant.single) {
315
- for (const suGet of suGets) {
308
+ for (const suGet of await suGets) {
316
309
  const suUser = await suGet();
317
310
  if (!suUser) continue;
318
311
  if (!await existUser(suUser)) continue;
@@ -338,7 +331,7 @@ async function getLoggedUser() {
338
331
  let exitUser = null;
339
332
  /** @type {string?} */
340
333
  let user = null;
341
- for (const [get, set, exit] of getLoggedUserFn) {
334
+ for (const [get, set, exit] of await getLoggedUserFn) {
342
335
  const u = await get();
343
336
  if (!u || !await existUser(u)) continue;
344
337
  user = u;
@@ -348,16 +341,16 @@ async function getLoggedUser() {
348
341
  }
349
342
  if (!user) return [
350
343
  null,
351
- defaultLogin,
344
+ await defaultLogin$1,
352
345
  null,
353
346
  null,
354
347
  null
355
348
  ];
356
- for (const suGet of suGets) {
349
+ for (const suGet of await suGets) {
357
350
  const suUser = await suGet();
358
351
  if (!suUser) continue;
359
352
  if (!await existUser(suUser)) continue;
360
- if (await switchable(tenant, user, suUser)) continue;
353
+ if (await (await switchableListPromise)(tenant, user, suUser)) continue;
361
354
  return [
362
355
  null,
363
356
  setUser,
@@ -374,7 +367,7 @@ async function getLoggedUser() {
374
367
  null
375
368
  ];
376
369
  }
377
- async function get() {
370
+ async function get$1() {
378
371
  let state = userGetter();
379
372
  if (!state) {
380
373
  state = getLoggedUser();
@@ -388,7 +381,7 @@ async function get() {
388
381
  */
389
382
  function isSingleUser(logged) {
390
383
  if (logged) return useTenant().then((g) => g.single);
391
- return get().then(([, , , su]) => su ? false : useTenant().then((g) => g.single));
384
+ return get$1().then(([, , , su]) => su ? false : useTenant().then((g) => g.single));
392
385
  }
393
386
  /**
394
387
  *
@@ -396,7 +389,7 @@ function isSingleUser(logged) {
396
389
  * @returns {Promise<string?>}
397
390
  */
398
391
  function getUser(logged) {
399
- return get().then(([, , , current, su]) => {
392
+ return get$1().then(([, , , current, su]) => {
400
393
  if (logged === false) return su;
401
394
  if (logged) return current;
402
395
  return su || current;
@@ -484,7 +477,7 @@ function setUser(userId, sudo) {
484
477
  */
485
478
  async function findUser(loginName, loginType) {
486
479
  const types = new Set([loginType || ""].flat().filter(Boolean));
487
- for (const fn of finders) {
480
+ for (const fn of await finders$1) {
488
481
  const userId = await fn(loginName, types);
489
482
  if (userId) return userId;
490
483
  }
@@ -499,6 +492,8 @@ async function findUser(loginName, loginType) {
499
492
  * @returns {Promise<number>}
500
493
  */
501
494
  async function verifyUser(userId, password, type, returnError) {
495
+ const verifiers = await verifiersPromise;
496
+ if (!Object.hasOwn(verifiers, type)) return 0;
502
497
  for (const fn of verifiers[type] || []) {
503
498
  const res = await fn(userId, password);
504
499
  if (res < 0) return returnError ? res : 0;
@@ -515,13 +510,15 @@ async function verifyUser(userId, password, type, returnError) {
515
510
  * @returns {Promise<boolean>}
516
511
  */
517
512
  async function setUserVerify(userId, type, password, deadline) {
513
+ const setVerifiers = await setVerifiersPromise;
514
+ if (!Object.hasOwn(setVerifiers, type)) return false;
518
515
  for (const fn of setVerifiers[type] || []) if (await fn(userId, password, deadline)) return true;
519
516
  return false;
520
517
  }
521
518
  /** @type {(user?: string?) => Promise<Set<string>>} */
522
519
  const loadUserPermissions = createKeyStore(async (user) => {
523
520
  if (!user) return /* @__PURE__ */ new Set();
524
- for (const f of getPermissions) {
521
+ for (const f of await getPermissions) {
525
522
  const r = await f(user);
526
523
  if (r) return new Set(r);
527
524
  }
@@ -553,7 +550,7 @@ async function hasUserPermission(permission, logged) {
553
550
  /** @type {(user?: string?) => Promise<Set<string>>} */
554
551
  const loadUserOrganizationAllPermissions = createKeyStore(async (user) => {
555
552
  if (!user) return /* @__PURE__ */ new Set();
556
- for (const f of getOrganizationAllPermissions) {
553
+ for (const f of await getOrganizationAllPermissions) {
557
554
  const r = await f(user);
558
555
  if (r) return new Set(r);
559
556
  }
@@ -576,7 +573,7 @@ async function getUserOrganizationAllPermissions(logged) {
576
573
  /** @type {(user?: string?) => Promise<Record<string, Set<string>>>} */
577
574
  const loadUserOrganizationPermissions = createKeyStore(async (user) => {
578
575
  if (!user) return Object.create(null);
579
- for (const f of getOrganizationPermissions) {
576
+ for (const f of await getOrganizationPermissions) {
580
577
  const r = await f(user);
581
578
  if (r) return Object.assign(Object.create(null), Object.fromEntries(Object.entries(r).map(([k, v]) => [k, new Set(v)])));
582
579
  }
@@ -631,13 +628,17 @@ async function hasUserOrganizationPermissions(permission, organization, logged)
631
628
  if (!permission) return true;
632
629
  return permissions.has(permission);
633
630
  }
631
+ const userPluginLoaders = userManagerMap.lazyThen((list) => {
632
+ return list.map(([k, v]) => [k, v?.loadPlugin]).filter(([, v]) => typeof v === "function");
633
+ });
634
634
  /**
635
635
  *
636
636
  * @param {string} userId
637
637
  * @returns {Promise<Record<string, any>>}
638
638
  */
639
639
  async function loadPluginUser(userId) {
640
- const list2 = await Promise.all(userPluginLoaders.map(([p, f]) => Promise.all([p, f(userId)])));
640
+ const loaders = await userPluginLoaders;
641
+ const list2 = await Promise.all(loaders.map(([p, f]) => Promise.all([p, f(userId)])));
641
642
  return Object.fromEntries(Object.entries(Object.groupBy(list2, ([k]) => k)).map(([k, l]) => {
642
643
  return [k, Object.assign({}, ...l?.map((v) => v[1]) || [])];
643
644
  }));
@@ -649,7 +650,7 @@ async function loadPluginUser(userId) {
649
650
  */
650
651
  async function loadUser(userId, plugin = false) {
651
652
  const [list, plugins, permissions, organizationPermissions] = await Promise.all([
652
- Promise.all(loaders.map((f) => f(userId))),
653
+ loaders.then((loaders) => Promise.all(loaders.map((f) => f(userId)))),
653
654
  plugin ? loadPluginUser(userId) : {},
654
655
  loadUserPermissions(userId).then((p) => [...p]),
655
656
  loadUserOrganizationPermissions(userId).then((p) => Object.fromEntries(Object.entries(p).map(([k, v]) => [k, [...v]])))
@@ -662,6 +663,160 @@ async function loadUser(userId, plugin = false) {
662
663
  });
663
664
  }
664
665
 
666
+ //#endregion
667
+ //#region packages/core/visitor.mjs
668
+ /** @import { VisitorManager } from './types.mjs' */
669
+ /**
670
+ * @typedef {[result: string | null, VisitorManager['set'] | null, VisitorManager['exit'] | null, visitor: string | null]} VisitorState
671
+ */
672
+ const [visitorGetter, visitorSetter] = createStoreSerializable("visitorStatus", async (state) => {
673
+ if (!state) return null;
674
+ return { visitor: (await state)[3] };
675
+ }, (val) => {
676
+ if (!val) return null;
677
+ const { visitor: visitorId } = val;
678
+ return Promise.resolve([
679
+ null,
680
+ null,
681
+ null,
682
+ visitorId
683
+ ]);
684
+ }, null);
685
+ const visitors = LazyPromise.try(async function() {
686
+ return (await Array.fromAsync(loadPluginProfiles("visitor"))).map((v) => v[1]);
687
+ });
688
+ const existTesters = visitors.lazyThen((v) => v.map((v) => v.exist).filter((v) => typeof v === "function"));
689
+ /**
690
+ *
691
+ * @param {string} visitorId
692
+ * @returns {Promise<boolean>}
693
+ */
694
+ async function existVisitor(visitorId) {
695
+ for (const fn of await existTesters) if (await fn(visitorId)) return true;
696
+ return false;
697
+ }
698
+ const getLoggedVisitorFn = visitors.lazyThen((list) => {
699
+ /** @type {[VisitorManager['get'], VisitorManager['set'] | null, VisitorManager['exit'] | null][]} */
700
+ const getLoggedVisitorFn = [];
701
+ for (const { get, set, exit } of list) {
702
+ if (typeof get !== "function") continue;
703
+ getLoggedVisitorFn.push([
704
+ get,
705
+ set || null,
706
+ exit || null
707
+ ]);
708
+ }
709
+ return getLoggedVisitorFn;
710
+ });
711
+ const defaultLogin = visitors.lazyThen((v) => v.find((v) => typeof v.login === "function")?.login || null);
712
+ /**
713
+ *
714
+ * @returns {Promise<VisitorState>}
715
+ */
716
+ async function getLoggedVisitor() {
717
+ /** @type {VisitorManager['set']?} */
718
+ let setVisitor = null;
719
+ /** @type {VisitorManager['exit']?} */
720
+ let exitVisitor = null;
721
+ /** @type {string?} */
722
+ let visitor = null;
723
+ for (const [get, set, exit] of await getLoggedVisitorFn) {
724
+ const u = await get();
725
+ if (!u || !await existVisitor(u)) continue;
726
+ visitor = u;
727
+ setVisitor = set;
728
+ exitVisitor = exit;
729
+ break;
730
+ }
731
+ if (!visitor) return [
732
+ null,
733
+ await defaultLogin,
734
+ null,
735
+ null
736
+ ];
737
+ return [
738
+ null,
739
+ setVisitor,
740
+ exitVisitor,
741
+ visitor
742
+ ];
743
+ }
744
+ async function get() {
745
+ let state = visitorGetter();
746
+ if (!state) {
747
+ state = getLoggedVisitor();
748
+ visitorSetter(state);
749
+ }
750
+ return state;
751
+ }
752
+ /**
753
+ *
754
+ * @returns {Promise<string?>}
755
+ */
756
+ function getVisitor() {
757
+ return get().then(([, , , current]) => current);
758
+ }
759
+ /**
760
+ *
761
+ * @param {string?} visitorId
762
+ * @returns {Promise<string?>}
763
+ */
764
+ function setVisitor(visitorId) {
765
+ let state = Promise.resolve(visitorGetter() || getLoggedVisitor());
766
+ state = state.then(async ([, set, exit, current]) => {
767
+ if (!visitorId) {
768
+ if (current && exit) await exit(current);
769
+ return [
770
+ null,
771
+ set,
772
+ exit,
773
+ null
774
+ ];
775
+ }
776
+ if (!set) return [
777
+ current,
778
+ set,
779
+ exit,
780
+ current
781
+ ];
782
+ if (!await set(visitorId)) return [
783
+ null,
784
+ set,
785
+ exit,
786
+ current
787
+ ];
788
+ return [
789
+ visitorId,
790
+ set,
791
+ exit,
792
+ visitorId
793
+ ];
794
+ });
795
+ visitorSetter(state);
796
+ return state.then((v) => v[0]);
797
+ }
798
+ const finders = visitors.lazyThen((v) => v.map((v) => v.find).filter((v) => typeof v === "function"));
799
+ /**
800
+ *
801
+ * @param {Record<string, unknown>} loginName
802
+ * @returns {Promise<string>}
803
+ */
804
+ async function findVisitor(loginName) {
805
+ for (const fn of await finders) {
806
+ const visitorId = await fn(loginName);
807
+ if (visitorId) return visitorId;
808
+ }
809
+ return "";
810
+ }
811
+ /**
812
+ *
813
+ * @param {string} visitorId
814
+ * @returns {Promise<Record<string, any>>}
815
+ */
816
+ async function loadVisitor(visitorId) {
817
+ return { id: visitorId };
818
+ }
819
+
665
820
  //#endregion
666
821
  //#region packages/core/permission/createPermissionMatches.mjs
667
822
  /** @import { Permission } from '@yongdall/types' */
@@ -957,7 +1112,6 @@ async function filterPermissionUpdate({ fields }, permissions, document, newDocu
957
1112
  * @returns
958
1113
  */
959
1114
  async function testPermissions(allPermissions, document) {
960
- if (await isSingleUser()) return true;
961
1115
  if (!allPermissions?.length) return false;
962
1116
  const permissionInGlobal = allPermissions.filter((v) => !v.organizationField);
963
1117
  const permissionInOrganization = allPermissions.filter((v) => v.organizationField);
@@ -1010,18 +1164,21 @@ async function testPermissions(allPermissions, document) {
1010
1164
  //#endregion
1011
1165
  //#region packages/core/decorators/modelHook.mjs
1012
1166
  /** @import { Hooks, ClassDecorator } from '@yongdall/model' */
1013
- /** @returns {{[K in keyof Hooks]: Record<string, Hooks[K][]>}} */
1014
- function loadHooks() {
1167
+ /** @import { ModelProfile } from '../types.mjs' */
1168
+ const modelHooksPromise = LazyPromise.try(async function() {
1015
1169
  /** @returns {{[K in keyof Hooks]: Record<string, Hooks[K][]>}} */
1016
1170
  const modelHooks = Object.create(null);
1017
- for (const [, model, v] of hooks.get("modelHook").entriesObject()) for (const [name, fn] of Object.entries(v)) {
1171
+ const list = await Array.fromAsync(loadPluginProfiles("model", function* ({ modelHook }) {
1172
+ if (!modelHook || typeof modelHook !== "object" || Array.isArray(modelHook)) return;
1173
+ yield* Object.entries(modelHook);
1174
+ }));
1175
+ for (const [model, v] of Object.entries(list)) for (const [name, fn] of Object.entries(v)) {
1018
1176
  if (typeof fn !== "function") continue;
1019
1177
  const list = modelHooks[name] ||= Object.create(null);
1020
1178
  (list[model] ||= []).push(fn);
1021
1179
  }
1022
1180
  return modelHooks;
1023
- }
1024
- const modelHooks = loadHooks();
1181
+ });
1025
1182
  /**
1026
1183
  *
1027
1184
  * @param {Partial<Hooks>} hooks
@@ -1035,6 +1192,7 @@ function bindModelHooks({ where, beforeCreateMany, afterCreateMany, beforeCreate
1035
1192
  where: where || (() => {}),
1036
1193
  async beforeCreateMany(conn, model, record) {
1037
1194
  let data = record;
1195
+ const modelHooks = await modelHooksPromise;
1038
1196
  for (const name of names) for (const hook of modelHooks.beforeCreateMany[name] || []) {
1039
1197
  if (typeof hook !== "function") continue;
1040
1198
  data = await hook(conn, model, data) || data;
@@ -1044,6 +1202,7 @@ function bindModelHooks({ where, beforeCreateMany, afterCreateMany, beforeCreate
1044
1202
  },
1045
1203
  async afterCreateMany(conn, model, record) {
1046
1204
  if (typeof afterCreateMany === "function") await afterCreateMany(conn, model, record);
1205
+ const modelHooks = await modelHooksPromise;
1047
1206
  for (const name of names) for (const hook of modelHooks.afterCreateMany[name] || []) {
1048
1207
  if (typeof hook !== "function") continue;
1049
1208
  await hook(conn, model, record);
@@ -1051,6 +1210,7 @@ function bindModelHooks({ where, beforeCreateMany, afterCreateMany, beforeCreate
1051
1210
  },
1052
1211
  async beforeCreate(conn, model, record) {
1053
1212
  let data = record;
1213
+ const modelHooks = await modelHooksPromise;
1054
1214
  for (const name of names) for (const hook of modelHooks.beforeCreate[name] || []) {
1055
1215
  if (typeof hook !== "function") continue;
1056
1216
  data = await hook(conn, model, data) || data;
@@ -1060,6 +1220,7 @@ function bindModelHooks({ where, beforeCreateMany, afterCreateMany, beforeCreate
1060
1220
  },
1061
1221
  async afterCreate(conn, model, record) {
1062
1222
  if (typeof afterCreate === "function") await afterCreate(conn, model, record);
1223
+ const modelHooks = await modelHooksPromise;
1063
1224
  for (const name of names) for (const hook of modelHooks.afterCreate[name] || []) {
1064
1225
  if (typeof hook !== "function") continue;
1065
1226
  await hook(conn, model, record);
@@ -1067,6 +1228,7 @@ function bindModelHooks({ where, beforeCreateMany, afterCreateMany, beforeCreate
1067
1228
  },
1068
1229
  async beforeUpdate(conn, model, record, set) {
1069
1230
  let data = set;
1231
+ const modelHooks = await modelHooksPromise;
1070
1232
  for (const name of names) for (const hook of modelHooks.beforeUpdate[name] || []) {
1071
1233
  if (typeof hook !== "function") continue;
1072
1234
  data = await hook(conn, model, record, data) || data;
@@ -1076,12 +1238,14 @@ function bindModelHooks({ where, beforeCreateMany, afterCreateMany, beforeCreate
1076
1238
  },
1077
1239
  async afterUpdate(conn, model, record, oldRecord) {
1078
1240
  if (typeof afterUpdate === "function") await afterUpdate(conn, model, record, oldRecord);
1241
+ const modelHooks = await modelHooksPromise;
1079
1242
  for (const name of names) for (const hook of modelHooks.afterUpdate[name] || []) {
1080
1243
  if (typeof hook !== "function") continue;
1081
1244
  await hook(conn, model, record, oldRecord);
1082
1245
  }
1083
1246
  },
1084
1247
  async beforeDelete(conn, model, record, update) {
1248
+ const modelHooks = await modelHooksPromise;
1085
1249
  for (const name of names) for (const hook of modelHooks.beforeDelete[name] || []) {
1086
1250
  if (typeof hook !== "function") continue;
1087
1251
  await hook(conn, model, record, update);
@@ -1090,6 +1254,7 @@ function bindModelHooks({ where, beforeCreateMany, afterCreateMany, beforeCreate
1090
1254
  },
1091
1255
  async afterDelete(conn, model, record, update) {
1092
1256
  if (typeof afterDelete === "function") await afterDelete(conn, model, record, update);
1257
+ const modelHooks = await modelHooksPromise;
1093
1258
  for (const name of names) for (const hook of modelHooks.afterDelete[name] || []) {
1094
1259
  if (typeof hook !== "function") continue;
1095
1260
  await hook(conn, model, record, update);
@@ -1109,6 +1274,7 @@ function modelHook(...hookNames) {
1109
1274
  if (!names.length) return;
1110
1275
  beforeCreateMany(Model, async (conn, model, record) => {
1111
1276
  let data = record;
1277
+ const modelHooks = await modelHooksPromise;
1112
1278
  for (const name of names) for (const hook of modelHooks.beforeCreateMany[name] || []) {
1113
1279
  if (typeof hook !== "function") continue;
1114
1280
  data = await hook(conn, model, data) || data;
@@ -1116,6 +1282,7 @@ function modelHook(...hookNames) {
1116
1282
  return data;
1117
1283
  });
1118
1284
  afterCreateMany(Model, async (conn, model, record) => {
1285
+ const modelHooks = await modelHooksPromise;
1119
1286
  for (const name of names) for (const hook of modelHooks.afterCreateMany[name] || []) {
1120
1287
  if (typeof hook !== "function") continue;
1121
1288
  await hook(conn, model, record);
@@ -1123,6 +1290,7 @@ function modelHook(...hookNames) {
1123
1290
  });
1124
1291
  beforeCreate(Model, async (conn, model, record) => {
1125
1292
  let data = record;
1293
+ const modelHooks = await modelHooksPromise;
1126
1294
  for (const name of names) for (const hook of modelHooks.beforeCreate[name] || []) {
1127
1295
  if (typeof hook !== "function") continue;
1128
1296
  data = await hook(conn, model, data) || data;
@@ -1130,6 +1298,7 @@ function modelHook(...hookNames) {
1130
1298
  return data;
1131
1299
  });
1132
1300
  afterCreate(Model, async (conn, model, record) => {
1301
+ const modelHooks = await modelHooksPromise;
1133
1302
  for (const name of names) for (const hook of modelHooks.afterCreate[name] || []) {
1134
1303
  if (typeof hook !== "function") continue;
1135
1304
  await hook(conn, model, record);
@@ -1137,6 +1306,7 @@ function modelHook(...hookNames) {
1137
1306
  });
1138
1307
  beforeUpdate(Model, async (conn, model, record, set) => {
1139
1308
  let data = set;
1309
+ const modelHooks = await modelHooksPromise;
1140
1310
  for (const name of names) for (const hook of modelHooks.beforeUpdate[name] || []) {
1141
1311
  if (typeof hook !== "function") continue;
1142
1312
  data = await hook(conn, model, record, data) || data;
@@ -1144,18 +1314,21 @@ function modelHook(...hookNames) {
1144
1314
  return data;
1145
1315
  });
1146
1316
  afterUpdate(Model, async (conn, model, record, oldRecord) => {
1317
+ const modelHooks = await modelHooksPromise;
1147
1318
  for (const name of names) for (const hook of modelHooks.afterUpdate[name] || []) {
1148
1319
  if (typeof hook !== "function") continue;
1149
1320
  await hook(conn, model, record, oldRecord);
1150
1321
  }
1151
1322
  });
1152
1323
  beforeDelete(Model, async (conn, model, record, update) => {
1324
+ const modelHooks = await modelHooksPromise;
1153
1325
  for (const name of names) for (const hook of modelHooks.beforeDelete[name] || []) {
1154
1326
  if (typeof hook !== "function") continue;
1155
1327
  await hook(conn, model, record, update);
1156
1328
  }
1157
1329
  });
1158
1330
  afterDelete(Model, async (conn, model, record, update) => {
1331
+ const modelHooks = await modelHooksPromise;
1159
1332
  for (const name of names) for (const hook of modelHooks.afterDelete[name] || []) {
1160
1333
  if (typeof hook !== "function") continue;
1161
1334
  await hook(conn, model, record, update);
@@ -1168,14 +1341,17 @@ function modelHook(...hookNames) {
1168
1341
  //#region packages/core/ServerError.mjs
1169
1342
  var ServerError = class extends Error {
1170
1343
  status = 500;
1344
+ code = "";
1171
1345
  /**
1172
1346
  *
1173
- * @param {string} message
1347
+ * @param {string | {code?: string; message: string; status?: number}} message
1174
1348
  * @param {number} [status]
1175
1349
  */
1176
1350
  constructor(message, status = 500) {
1177
- super(message);
1178
- this.status = status;
1351
+ super(typeof message === "string" ? message : message.message);
1352
+ const s = typeof message === "object" && message.status || status;
1353
+ this.status = s;
1354
+ this.code = typeof message === "object" && message.code || String(s);
1179
1355
  }
1180
1356
  };
1181
1357
 
@@ -1360,20 +1536,24 @@ function toDocumentFields(fields) {
1360
1536
  //#endregion
1361
1537
  //#region packages/core/enumerations.mjs
1362
1538
  /** @import { MaybePromise } from './types.mjs' */
1363
- /** @type {Record<string, (Record<string, any> | (() => MaybePromise<Record<string, any>[]>))[]>} */
1364
- let enumerations = {};
1365
- hooks.listen(() => {
1366
- const groups = Object.groupBy([...hooks.get("enumerations").values()].flatMap((v) => Object.entries(v)), ([k]) => k);
1367
- enumerations = Object.fromEntries(Object.entries(groups).map(([k, v]) => [k, v?.map((v) => v[1]).flat(2) || []]));
1368
- }, -1e6);
1539
+ /** @type {Promise<Record<string, (Record<string, any> | (() => MaybePromise<Record<string, any>[]>))[]>>} */
1540
+ const enumerationsPromise = LazyPromise.try(async function() {
1541
+ const list = await Array.fromAsync(loadPluginProfiles("enumeration", function* (c) {
1542
+ yield* c && typeof c === "object" && !Array.isArray(c) ? Object.entries(c) : [];
1543
+ }));
1544
+ const groups = Object.groupBy(list, ([k]) => k);
1545
+ return Object.fromEntries(Object.entries(groups).map(([k, v]) => [k, v?.map((v) => v[1]).flat(2) || []]));
1546
+ });
1369
1547
  /**
1370
1548
  *
1371
1549
  * @param {string} name
1372
1550
  * @returns {Promise<Record<string, any>[]>}
1373
1551
  */
1374
1552
  async function getEnumerations(name) {
1375
- if (!Object.hasOwn(enumerations, name)) return [];
1376
- return (await Promise.all(enumerations[name].map((v) => typeof v === "function" ? v() : v))).flat();
1553
+ const allEnumerations = await enumerationsPromise;
1554
+ if (!Object.hasOwn(allEnumerations, name)) return [];
1555
+ const enumerations = allEnumerations[name].map((v) => typeof v === "function" ? v() : v);
1556
+ return await Promise.all(enumerations).then((v) => v.flat());
1377
1557
  }
1378
1558
 
1379
1559
  //#endregion
@@ -1734,6 +1914,39 @@ async function search2where(fields, and, or, or2) {
1734
1914
  }, fields, and, or, or2), execWhere, null);
1735
1915
  }
1736
1916
 
1917
+ //#endregion
1918
+ //#region packages/core/safe.mjs
1919
+ /**
1920
+ * 使用 HMAC-SHA256 对一个或多个值进行哈希,使用 salt 作为密钥。
1921
+ * 输出为 base64url 格式。
1922
+ *
1923
+ * @param {string | ArrayBuffer | ArrayBufferView<ArrayBuffer>} salt
1924
+ * @param {string | number} value
1925
+ * @param {...(string | number)} values
1926
+ * @returns {Promise<string>}
1927
+ */
1928
+ async function hashSHA256(salt, value, ...values) {
1929
+ /** @type {ArrayBuffer | ArrayBufferView<ArrayBuffer>} */
1930
+ let keyBuffer;
1931
+ if (salt instanceof ArrayBuffer || ArrayBuffer.isView(salt)) keyBuffer = salt;
1932
+ else if (typeof salt === "string") keyBuffer = new TextEncoder().encode(salt);
1933
+ else throw new Error("`salt` 参数必须为字符串或 Uint8Array");
1934
+ const cryptoKey = await crypto.subtle.importKey("raw", keyBuffer, {
1935
+ name: "HMAC",
1936
+ hash: "SHA-256"
1937
+ }, false, ["sign"]);
1938
+ /** @type {string[]} */
1939
+ let data = [];
1940
+ if (typeof value === "number") data.push(String(value));
1941
+ else if (typeof value === "string") data.push(JSON.stringify(value).slice(1, -1));
1942
+ else throw new Error("`value` 参数必须为字符串或数字");
1943
+ for (const v of values) if (typeof v === "number") data.push(String(v));
1944
+ else if (typeof v === "string") data.push(JSON.stringify(v).slice(1, -1));
1945
+ const messageBuffer = new TextEncoder().encode(data.join("\n"));
1946
+ const signature = await crypto.subtle.sign("HMAC", cryptoKey, messageBuffer);
1947
+ return btoa(String.fromCharCode(...new Uint8Array(signature))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
1948
+ }
1949
+
1737
1950
  //#endregion
1738
1951
  //#region packages/core/index.mjs
1739
1952
  /**
@@ -1749,5 +1962,5 @@ function initSystem(tenant, boot, pluginHooks) {
1749
1962
  }
1750
1963
 
1751
1964
  //#endregion
1752
- export { ServerError, bindModelHooks, bootConfiguration, createPermissionMatches, initSystem as default, existUser, filterPermissionDocument, filterPermissionUpdate, filterPermissions, findModel, findUser, getEnumerations, getMenus, getModel, getModelAuthorizationPermissions, getModelPermissions, getUser, getUserOrganizationAllPermissions, getUserOrganizationPermissions, getUserPermissions, hasUserOrganizationPermissions, hasUserPermission, hooks, initConfig, isSingleUser, loadPluginUser, loadUser, loadUserOrganizationAllPermissions, loadUserOrganizationPermissions, loadUserPermissions, mainTenant, modelHook, modelLoaders, models, nestedSetTree, search2where, setUser, setUserVerify, testPermissionConstraints, testPermissions, toDocumentFields, useTenant, verifyUser };
1965
+ export { ServerError, bindModelHooks, bootConfiguration, createPermissionMatches, initSystem as default, existUser, existVisitor, filterPermissionDocument, filterPermissionUpdate, filterPermissions, findModel, findUser, findVisitor, getEnumerations, getMenus, getModel, getModelAuthorizationPermissions, getModelPermissions, getUser, getUserOrganizationAllPermissions, getUserOrganizationPermissions, getUserPermissions, getVisitor, hasUserOrganizationPermissions, hasUserPermission, hashSHA256, hooks, initConfig, isSingleUser, loadPluginUser, loadUser, loadUserOrganizationAllPermissions, loadUserOrganizationPermissions, loadUserPermissions, loadVisitor, mainTenant, modelHook, nestedSetTree, search2where, setUser, setUserVerify, setVisitor, testPermissionConstraints, testPermissions, toDocumentFields, useTenant, verifyUser };
1753
1966
  //# sourceMappingURL=index.mjs.map