@yongdall/core 0.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/index.mjs ADDED
@@ -0,0 +1,1709 @@
1
+ import { Hook, Search, isFunction } from "@yongdall/common";
2
+ import { Query, Where, afterCreate, afterCreateMany, afterDelete, afterUpdate, beforeCreate, beforeCreateMany, beforeDelete, beforeUpdate, coefficient, decrement, expandModel, getDefinePermission, increment, multiply } from "@yongdall/model";
3
+ import { createKeyStore, createStoreSerializable } from "@yongdall/context";
4
+
5
+ //#region packages/core/polyfill.mjs
6
+ if (!Symbol.metadata) Symbol.metadata = Symbol.for("Symbol.metadata");
7
+
8
+ //#endregion
9
+ //#region packages/core/config.mjs
10
+ /** @import { Tenant } from '@yongdall/types' */
11
+ /** @import { BootConfiguration } from '@yongdall/common' */
12
+ /** @type {Tenant} */
13
+ const mainTenant = {
14
+ id: "",
15
+ label: "拥道YongDall",
16
+ single: false,
17
+ providers: {},
18
+ salt: "YongDall"
19
+ };
20
+ /** @type {BootConfiguration} */
21
+ const bootConfiguration = {
22
+ pages: {},
23
+ theme: "@yongdall/theme#allSider",
24
+ authenticator: "",
25
+ path: "/workbench/"
26
+ };
27
+ /**
28
+ *
29
+ * @param {Partial<Omit<Tenant, 'id'>>} tenant
30
+ * @param {object} boot
31
+ * @returns
32
+ */
33
+ function initConfig({ label, single, providers, salt }, boot) {
34
+ Object.assign(bootConfiguration, boot);
35
+ mainTenant.label = typeof label === "string" && label || "拥道YongDall";
36
+ mainTenant.single = Boolean(single);
37
+ if (salt) mainTenant.salt = salt;
38
+ mainTenant.providers = Object.fromEntries(Object.entries(typeof providers === "object" && providers || {}).filter(([k, v]) => typeof v === "string" && v));
39
+ }
40
+
41
+ //#endregion
42
+ //#region packages/core/hooks.mjs
43
+ /** @import { Hooks } from './types.mjs' */
44
+ /** @type {Hook<Hooks>} */
45
+ const hooks = new Hook();
46
+
47
+ //#endregion
48
+ //#region packages/core/menu.mjs
49
+ /**
50
+ *
51
+ * @param {string} type
52
+ * @param {string} [group]
53
+ * @returns
54
+ */
55
+ function getMenus(type, group = "") {
56
+ return [];
57
+ }
58
+
59
+ //#endregion
60
+ //#region packages/core/models.mjs
61
+ /** @import { Permission } from '@yongdall/types' */
62
+ /** @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) => {
80
+ if (!v || typeof v !== "object" || Array.isArray(v)) return [];
81
+ return Object.entries(v).map(([fieldName, field]) => [
82
+ modelId,
83
+ fieldName,
84
+ field
85
+ ]);
86
+ }));
87
+ const groups = Object.groupBy(modelFields, (v) => v[0]);
88
+ /** @type {Record<string, Fields<TableDefine>>} */
89
+ const expands = {};
90
+ for (const [modelId, fields] of Object.entries(groups)) {
91
+ if (!fields?.length) continue;
92
+ expands[modelId] = Object.fromEntries(fields.map(([, fieldName, field]) => [fieldName, field]));
93
+ }
94
+ modelExpands = expands;
95
+ }, -1e6);
96
+ /**
97
+ *
98
+ * @param {string | string[]} ident
99
+ * @returns {Promise<ModelTable?>}
100
+ */
101
+ async function findModel(ident) {
102
+ const [model, ...collection] = (Array.isArray(ident) ? ident : ident.split(":")).filter(Boolean);
103
+ if (!collection.length) {
104
+ const define = Object.hasOwn(models, model) && models[model];
105
+ return typeof define === "object" && define || null;
106
+ }
107
+ const loader = Object.hasOwn(modelLoaders, model) && modelLoaders[model];
108
+ if (typeof loader !== "function") return null;
109
+ const define = await loader(collection);
110
+ return typeof define === "object" && define || null;
111
+ }
112
+ /**
113
+ *
114
+ * @param {string | string[]} ident
115
+ * @returns {Promise<ModelTable?>}
116
+ */
117
+ async function getModel(ident) {
118
+ const model = await findModel(ident);
119
+ if (!model) return model;
120
+ const define = modelMap.get(model);
121
+ if (define) return define;
122
+ const modelIdent = Array.isArray(ident) ? ident.join(":") : ident;
123
+ const newDefine = expandModel(model, Object.hasOwn(modelExpands, modelIdent) ? modelExpands[modelIdent] : {});
124
+ modelMap.set(model, newDefine);
125
+ return newDefine;
126
+ }
127
+ /** @type {(modelId: string, groupId?: string?) => Promise<Permission[]?>} */
128
+ const getModelPermissions = createKeyStore(async (modelId, groupId) => {
129
+ for (const fn of getModelPermissionsHooks) {
130
+ const permissions = await fn(modelId, groupId);
131
+ if (permissions) return permissions;
132
+ }
133
+ if (groupId) return null;
134
+ return getDefinePermission(await getModel(modelId));
135
+ }, (modelId, groupId) => [modelId, groupId || ""].join("\n"));
136
+ /**
137
+ *
138
+ * @param {string} modelId
139
+ * @param {string} authorization
140
+ * @param {string?} [groupId]
141
+ * @returns {Promise<Permission[]?>}
142
+ */
143
+ async function getModelAuthorizationPermissions(modelId, authorization, groupId) {
144
+ const permissions = await getModelPermissions(modelId, groupId);
145
+ if (!permissions?.length) return permissions;
146
+ return permissions.filter(({ authorizations }) => {
147
+ if (!authorizations) return false;
148
+ if (Array.isArray(authorizations)) return authorizations.includes(authorization);
149
+ if (authorizations instanceof Set) return authorizations.has(authorization);
150
+ return false;
151
+ });
152
+ }
153
+
154
+ //#endregion
155
+ //#region packages/core/tenant.mjs
156
+ /** @import { Tenant } from '@yongdall/types' */
157
+ const [tenantGetter, tenantSetter] = createStoreSerializable("tenantStatus", async (state) => {
158
+ if (!state) return null;
159
+ return state;
160
+ }, (tenant) => {
161
+ return tenant ? Promise.resolve(tenant) : null;
162
+ }, null);
163
+ function getTenantManager() {
164
+ return [...hooks.get("tenantManager").values()].filter(Boolean);
165
+ }
166
+ let tenantManagers = getTenantManager();
167
+ hooks.listen(() => {
168
+ tenantManagers = getTenantManager();
169
+ }, -1e6);
170
+ /**
171
+ *
172
+ * @returns {Promise<Tenant>}
173
+ */
174
+ async function getCurrentTenant() {
175
+ if (!tenantManagers.length) return mainTenant;
176
+ for (const get of tenantManagers) {
177
+ const u = await get();
178
+ if (!u) continue;
179
+ return u;
180
+ }
181
+ throw new Error("[noTenant] 找不到租户");
182
+ }
183
+ async function get$1() {
184
+ let state = tenantGetter();
185
+ if (!state) {
186
+ state = getCurrentTenant();
187
+ tenantSetter(state);
188
+ }
189
+ return state;
190
+ }
191
+ /**
192
+ *
193
+ * @returns {Promise<Tenant>}
194
+ */
195
+ function useTenant() {
196
+ return get$1();
197
+ }
198
+
199
+ //#endregion
200
+ //#region packages/core/user.mjs
201
+ /** @import { Tenant } from '@yongdall/types' */
202
+ /** @import { UserManager, MaybePromise } from './types.mjs' */
203
+ /**
204
+ * @typedef {[result: string | null, UserManager['set'] | null, UserManager['exit'] | null, user: string | null, sUser: string | null]} State
205
+ */
206
+ const [userGetter, userSetter] = createStoreSerializable("userStatus", async (state) => {
207
+ if (!state) return null;
208
+ const s = await state;
209
+ return {
210
+ user: s[3],
211
+ sUser: s[4]
212
+ };
213
+ }, (val) => {
214
+ if (!val) return null;
215
+ const { user: userId, sUser: sUserId } = val;
216
+ return Promise.resolve([
217
+ null,
218
+ null,
219
+ null,
220
+ userId,
221
+ sUserId
222
+ ]);
223
+ }, 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;
231
+ 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
+ /**
264
+ * @param {Tenant} tenant
265
+ * @param {string} user
266
+ * @param {string} sUser
267
+ * @returns {Promise<boolean>}
268
+ */
269
+ async function switchable(tenant, user, sUser) {
270
+ if (!switchableList.length) return false;
271
+ for (const s of switchableList) {
272
+ if (await s(user, sUser)) continue;
273
+ return false;
274
+ }
275
+ 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
+ };
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);
298
+ /**
299
+ *
300
+ * @param {string} userId
301
+ * @param {boolean} [login]
302
+ * @returns {Promise<boolean>}
303
+ */
304
+ async function existUser(userId, login) {
305
+ for (const fn of existTesters) if (await fn(userId, login)) return true;
306
+ return false;
307
+ }
308
+ /**
309
+ *
310
+ * @returns {Promise<State>}
311
+ */
312
+ async function getLoggedUser() {
313
+ const tenant = await useTenant();
314
+ if (tenant.single) {
315
+ for (const suGet of suGets) {
316
+ const suUser = await suGet();
317
+ if (!suUser) continue;
318
+ if (!await existUser(suUser)) continue;
319
+ return [
320
+ null,
321
+ null,
322
+ null,
323
+ null,
324
+ suUser
325
+ ];
326
+ }
327
+ return [
328
+ null,
329
+ null,
330
+ null,
331
+ null,
332
+ null
333
+ ];
334
+ }
335
+ /** @type {UserManager['set']?} */
336
+ let setUser = null;
337
+ /** @type {UserManager['exit']?} */
338
+ let exitUser = null;
339
+ /** @type {string?} */
340
+ let user = null;
341
+ for (const [get, set, exit] of getLoggedUserFn) {
342
+ const u = await get();
343
+ if (!u || !await existUser(u)) continue;
344
+ user = u;
345
+ setUser = set;
346
+ exitUser = exit;
347
+ break;
348
+ }
349
+ if (!user) return [
350
+ null,
351
+ defaultLogin,
352
+ null,
353
+ null,
354
+ null
355
+ ];
356
+ for (const suGet of suGets) {
357
+ const suUser = await suGet();
358
+ if (!suUser) continue;
359
+ if (!await existUser(suUser)) continue;
360
+ if (await switchable(tenant, user, suUser)) continue;
361
+ return [
362
+ null,
363
+ setUser,
364
+ exitUser,
365
+ user,
366
+ suUser
367
+ ];
368
+ }
369
+ return [
370
+ null,
371
+ setUser,
372
+ exitUser,
373
+ user,
374
+ null
375
+ ];
376
+ }
377
+ async function get() {
378
+ let state = userGetter();
379
+ if (!state) {
380
+ state = getLoggedUser();
381
+ userSetter(state);
382
+ }
383
+ return state;
384
+ }
385
+ /**
386
+ * @param {boolean} [logged]
387
+ * @returns {Promise<boolean>}
388
+ */
389
+ function isSingleUser(logged) {
390
+ if (logged) return useTenant().then((g) => g.single);
391
+ return get().then(([, , , su]) => su ? false : useTenant().then((g) => g.single));
392
+ }
393
+ /**
394
+ *
395
+ * @param {boolean?} [logged] 是否为登陆用户
396
+ * @returns {Promise<string?>}
397
+ */
398
+ function getUser(logged) {
399
+ return get().then(([, , , current, su]) => {
400
+ if (logged === false) return su;
401
+ if (logged) return current;
402
+ return su || current;
403
+ });
404
+ }
405
+ /**
406
+ *
407
+ * @param {string?} userId
408
+ * @param {boolean?} [sudo]
409
+ * @returns {Promise<string?>}
410
+ */
411
+ function setUser(userId, sudo) {
412
+ let state = Promise.resolve(userGetter() || getLoggedUser());
413
+ if (sudo) state = state.then(async ([, set, exit, current]) => {
414
+ if (!userId || !await existUser(userId)) return [
415
+ null,
416
+ set,
417
+ exit,
418
+ current,
419
+ null
420
+ ];
421
+ if (current || await useTenant().then((t) => t.single)) return [
422
+ userId,
423
+ set,
424
+ exit,
425
+ current,
426
+ userId
427
+ ];
428
+ return [
429
+ null,
430
+ set,
431
+ exit,
432
+ null,
433
+ null
434
+ ];
435
+ });
436
+ else state = state.then(async ([, set, exit, current, su]) => {
437
+ if (!userId) {
438
+ if (current && exit) await exit(current);
439
+ return [
440
+ null,
441
+ set,
442
+ exit,
443
+ null,
444
+ null
445
+ ];
446
+ }
447
+ if (!set) return [
448
+ current,
449
+ set,
450
+ exit,
451
+ current,
452
+ su
453
+ ];
454
+ if ((await useTenant()).single) return [
455
+ null,
456
+ set,
457
+ exit,
458
+ null,
459
+ su
460
+ ];
461
+ if (!await set(userId)) return [
462
+ null,
463
+ set,
464
+ exit,
465
+ current,
466
+ su
467
+ ];
468
+ return [
469
+ userId,
470
+ set,
471
+ exit,
472
+ userId,
473
+ null
474
+ ];
475
+ });
476
+ userSetter(state);
477
+ return state.then((v) => v[0]);
478
+ }
479
+ /**
480
+ *
481
+ * @param {string | Record<string, unknown>} loginName
482
+ * @param {string | string[]} [loginType]
483
+ * @returns {Promise<string>}
484
+ */
485
+ async function findUser(loginName, loginType) {
486
+ const types = new Set([loginType || ""].flat().filter(Boolean));
487
+ for (const fn of finders) {
488
+ const userId = await fn(loginName, types);
489
+ if (userId) return userId;
490
+ }
491
+ return "";
492
+ }
493
+ /**
494
+ *
495
+ * @param {string} userId
496
+ * @param {string} password
497
+ * @param {string} type
498
+ * @param {boolean} [returnError]
499
+ * @returns {Promise<number>}
500
+ */
501
+ async function verifyUser(userId, password, type, returnError) {
502
+ for (const fn of verifiers[type] || []) {
503
+ const res = await fn(userId, password);
504
+ if (res < 0) return returnError ? res : 0;
505
+ if (res > 0) return res;
506
+ }
507
+ return 0;
508
+ }
509
+ /**
510
+ *
511
+ * @param {string} userId
512
+ * @param {string} type
513
+ * @param {string?} password
514
+ * @param {Date?} [deadline]
515
+ * @returns {Promise<boolean>}
516
+ */
517
+ async function setUserVerify(userId, type, password, deadline) {
518
+ for (const fn of setVerifiers[type] || []) if (await fn(userId, password, deadline)) return true;
519
+ return false;
520
+ }
521
+ /** @type {(user?: string?) => Promise<Set<string>>} */
522
+ const loadUserPermissions = createKeyStore(async (user) => {
523
+ if (!user) return /* @__PURE__ */ new Set();
524
+ for (const f of getPermissions) {
525
+ const r = await f(user);
526
+ if (r) return new Set(r);
527
+ }
528
+ return /* @__PURE__ */ new Set();
529
+ }, (user) => user || "");
530
+ /**
531
+ *
532
+ * @param {boolean?} [logged] 是否为登陆用户
533
+ * @returns {Promise<Set<string>>}
534
+ */
535
+ async function getUserPermissions(logged) {
536
+ return loadUserPermissions(await getUser(logged));
537
+ }
538
+ /** @type {(user?: string?) => Promise<Set<string>>} */
539
+ const loadUserOrganizationAllPermissions = createKeyStore(async (user) => {
540
+ if (!user) return /* @__PURE__ */ new Set();
541
+ for (const f of getOrganizationAllPermissions) {
542
+ const r = await f(user);
543
+ if (r) return new Set(r);
544
+ }
545
+ return /* @__PURE__ */ new Set();
546
+ }, (user) => user || "");
547
+ /**
548
+ *
549
+ * @param {boolean?} [logged] 是否为登陆用户
550
+ * @returns {Promise<Set<string>>}
551
+ */
552
+ async function getUserOrganizationAllPermissions(logged) {
553
+ return loadUserOrganizationAllPermissions(await getUser(logged));
554
+ }
555
+ /** @type {(user?: string?) => Promise<Record<string, Set<string>>>} */
556
+ const loadUserOrganizationPermissions = createKeyStore(async (user) => {
557
+ if (!user) return Object.create(null);
558
+ for (const f of getOrganizationPermissions) {
559
+ const r = await f(user);
560
+ if (r) return Object.fromEntries(Object.entries(r).map(([k, v]) => [k, new Set(v)]));
561
+ }
562
+ return Object.create(null);
563
+ }, (user) => user || "");
564
+ /**
565
+ *
566
+ * @param {boolean?} [logged] 是否为登陆用户
567
+ * @returns {Promise<Record<string, Set<string>>>}
568
+ */
569
+ async function getUserOrganizationPermissions(logged) {
570
+ return loadUserOrganizationPermissions(await getUser(logged));
571
+ }
572
+ /**
573
+ *
574
+ * @param {string} userId
575
+ * @returns {Promise<Record<string, any>>}
576
+ */
577
+ async function loadPluginUser(userId) {
578
+ const list2 = await Promise.all(userPluginLoaders.map(([p, f]) => Promise.all([p, f(userId)])));
579
+ return Object.fromEntries(Object.entries(Object.groupBy(list2, ([k]) => k)).map(([k, l]) => {
580
+ return [k, Object.assign({}, ...l?.map((v) => v[1]) || [])];
581
+ }));
582
+ }
583
+ /**
584
+ *
585
+ * @param {string} userId
586
+ * @returns {Promise<Record<string, any>>}
587
+ */
588
+ async function loadUser(userId, plugin = false) {
589
+ const [list, plugins, permissions] = await Promise.all([
590
+ Promise.all(loaders.map((f) => f(userId))),
591
+ plugin ? loadPluginUser(userId) : {},
592
+ loadUserPermissions(userId).then((p) => [...p])
593
+ ]);
594
+ return Object.assign({}, ...list, {
595
+ id: userId,
596
+ plugins,
597
+ permissions
598
+ });
599
+ }
600
+
601
+ //#endregion
602
+ //#region packages/core/permission/createPermissionMatches.mjs
603
+ /** @import { Permission } from '@yongdall/types' */
604
+ /**
605
+ *
606
+ * @param {Exclude<Permission['constraints'], void>?} [constraints]
607
+ * @param {string?} [userId]
608
+ * @returns {Iterable<[string, string, any]>}
609
+ */
610
+ function* yieldConstraintWheres(constraints, userId) {
611
+ if (!constraints) return;
612
+ for (const [field, constraint] of Object.entries(constraints)) {
613
+ if (!constraint) continue;
614
+ const { value, values, user } = constraint;
615
+ if (value !== void 0) yield [
616
+ field,
617
+ "=",
618
+ value
619
+ ];
620
+ else if (Array.isArray(values) && values.length) yield [
621
+ field,
622
+ "in",
623
+ values
624
+ ];
625
+ else if (values instanceof Set && values.size) yield [
626
+ field,
627
+ "in",
628
+ [...values]
629
+ ];
630
+ else if (user && userId) yield [
631
+ field,
632
+ "=",
633
+ userId
634
+ ];
635
+ }
636
+ }
637
+ /**
638
+ * @param {Permission[]} permissions
639
+ */
640
+ async function createPermissionMatches(permissions) {
641
+ let permissionInGlobal = permissions.filter((v) => !v.organizationField);
642
+ const orWhere = new Where();
643
+ if (permissionInGlobal.length) {
644
+ const permissions = await getUserPermissions();
645
+ permissionInGlobal = permissionInGlobal.filter((v) => !v.permission || permissions.has(v.permission));
646
+ }
647
+ const userId = await getUser();
648
+ for (const { constraints } of permissionInGlobal) {
649
+ const wheres = [...yieldConstraintWheres(constraints, userId)];
650
+ if (!wheres.length) return null;
651
+ const where = new Where();
652
+ for (const v of wheres) where.and(...v);
653
+ orWhere.or(where);
654
+ }
655
+ let permissionInOrganization = permissions.filter((v) => v.organizationField);
656
+ if (permissionInOrganization.length) {
657
+ const organizationPermissions = await getUserOrganizationAllPermissions();
658
+ permissionInOrganization = permissionInOrganization.filter((v) => !v.permission || organizationPermissions.has(v.permission));
659
+ }
660
+ if (!permissionInOrganization.length) return orWhere;
661
+ if (permissionInOrganization.length) {
662
+ const organizationPermissions = Object.entries(await getUserOrganizationPermissions());
663
+ for (const { constraints, permission, organizationField } of permissionInOrganization) {
664
+ const organizations = permission ? organizationPermissions.filter((v) => v[1].has(permission)).map((v) => v[0]) : organizationPermissions.map((v) => v[0]);
665
+ if (!organizations.length) continue;
666
+ const where = Where.and(organizationField || "", "in", organizations);
667
+ for (const v of yieldConstraintWheres(constraints, userId)) where.and(...v);
668
+ orWhere.or(where);
669
+ }
670
+ }
671
+ return orWhere;
672
+ }
673
+
674
+ //#endregion
675
+ //#region packages/core/permission/testPermissionConstraints.mjs
676
+ /** @import { Permission } from '@yongdall/types' */
677
+ /**
678
+ *
679
+ * @param {Exclude<Permission['constraints'], void>} constraints
680
+ * @param {Record<string, any>} document
681
+ * @param {string?} userId
682
+ */
683
+ function testPermissionConstraints(constraints, document, userId) {
684
+ for (const [field, constraint] of Object.entries(constraints)) {
685
+ if (!constraint) continue;
686
+ const { value, values, user } = constraint;
687
+ const val = document[field];
688
+ if (value !== void 0) {
689
+ if (val !== value) return false;
690
+ } else if (Array.isArray(values) && values.length) {
691
+ if (!values.includes(val)) return false;
692
+ } else if (values instanceof Set && values.size) {
693
+ if (!values.has(val)) return false;
694
+ } else if (user) {
695
+ if (val !== userId) return false;
696
+ }
697
+ }
698
+ return true;
699
+ }
700
+
701
+ //#endregion
702
+ //#region packages/core/permission/filterPermissions.mjs
703
+ /** @import { Permission } from '@yongdall/types' */
704
+ /**
705
+ *
706
+ * @param {Permission[]} allPermissions
707
+ * @param {Record<string, any>} document
708
+ * @returns
709
+ */
710
+ async function filterPermissions(allPermissions, document) {
711
+ const permissionInGlobal = allPermissions.filter((v) => !v.organizationField);
712
+ const permissionInOrganization = allPermissions.filter((v) => v.organizationField);
713
+ /** @type {Permission[]} */
714
+ const filteredPermissions = [];
715
+ const userId = await getUser();
716
+ if (permissionInGlobal.length) {
717
+ const permissions = await getUserPermissions();
718
+ for (const p of permissionInGlobal) {
719
+ const { permission, constraints } = p;
720
+ if (permission && !permissions.has(permission)) continue;
721
+ if (constraints && !testPermissionConstraints(constraints, document, userId)) continue;
722
+ filteredPermissions.push(p);
723
+ }
724
+ }
725
+ if (permissionInOrganization.length) {
726
+ const permissions = await getUserOrganizationPermissions();
727
+ for (const p of permissionInOrganization) {
728
+ const { permission, organizationField, constraints } = p;
729
+ if (constraints && !testPermissionConstraints(constraints, document, userId)) continue;
730
+ const organizationId = document[organizationField || ""];
731
+ if (!organizationId) continue;
732
+ const organizationPermissions = permissions[organizationId];
733
+ if (!organizationPermissions) continue;
734
+ if (permission && !organizationPermissions.has(permission)) continue;
735
+ filteredPermissions.push(p);
736
+ }
737
+ }
738
+ return filteredPermissions;
739
+ }
740
+
741
+ //#endregion
742
+ //#region packages/core/permission/filterPermissionDocument.mjs
743
+ /** @import { Permission } from '@yongdall/types' */
744
+ /** @import { ModelTable, FieldDefine } from '@yongdall/model' */
745
+ /**
746
+ * @param {ModelTable} model
747
+ * @param {Permission[]} permissions
748
+ * @param {Record<string, any>} document
749
+ */
750
+ async function filterPermissionDocument({ fields }, permissions, document) {
751
+ const userPermissions = await filterPermissions(permissions, document);
752
+ if (userPermissions && !userPermissions.length) return null;
753
+ let groups = new Set(userPermissions.flatMap((v) => v.group || ""));
754
+ let fieldsAuth = new Set(userPermissions.flatMap((v) => v.fields || ""));
755
+ groups.delete("");
756
+ fieldsAuth.delete("");
757
+ if (!(groups.size + fieldsAuth.size)) return null;
758
+ /**
759
+ *
760
+ * @param {[string, FieldDefine & {group?: string}][]} fields
761
+ * @param {Record<string, any>} document
762
+ * @param {string[]} parent
763
+ * @returns
764
+ */
765
+ function filter(fields, document, parent) {
766
+ /** @type {Record<string, any>} */
767
+ const value = {};
768
+ for (const [name, field] of fields) {
769
+ if (!field) continue;
770
+ const fieldNames = [...parent, name];
771
+ const key = fieldNames.join(".");
772
+ if (!field.primary && !groups.has(field.group || "") && !fieldsAuth.has(key) && !fieldsAuth.has("*")) continue;
773
+ const { type } = field;
774
+ if (!type || typeof type !== "object" || !type.table) {
775
+ value[name] = document[name];
776
+ continue;
777
+ }
778
+ const subDocument = document[name];
779
+ const list = Object.entries(type.fields);
780
+ if (field.array) value[name] = (Array.isArray(subDocument) ? subDocument : subDocument ? [subDocument] : []).map((doc) => filter(list, doc, key));
781
+ else if (subDocument && !Array.isArray(subDocument)) {
782
+ const val = filter(list, subDocument, fieldNames);
783
+ if (!Object.keys(val)) continue;
784
+ value[name] = val;
785
+ }
786
+ }
787
+ return value;
788
+ }
789
+ return filter(Object.entries(fields), document, []);
790
+ }
791
+
792
+ //#endregion
793
+ //#region packages/core/permission/filterPermissionUpdate.mjs
794
+ /** @import { FieldDefine, ModelTable } from '@yongdall/model' */
795
+ /** @import { Permission } from '@yongdall/types' */
796
+ /**
797
+ * @param {ModelTable} model
798
+ * @param {Permission[]} permissions
799
+ * @param {Record<string, any>} document
800
+ * @param {Record<string, any>} newDocument
801
+ */
802
+ async function filterPermissionUpdate({ fields }, permissions, document, newDocument) {
803
+ let newData = newDocument;
804
+ const userPermissions = await filterPermissions(permissions, {
805
+ ...document,
806
+ ...newData
807
+ });
808
+ if (userPermissions && !userPermissions.length) return null;
809
+ let groups = new Set(userPermissions.flatMap((v) => v.group || ""));
810
+ let fieldsAuth = new Set(userPermissions.flatMap((v) => v.fields || ""));
811
+ groups.delete("");
812
+ fieldsAuth.delete("");
813
+ while (groups.size + fieldsAuth.size) {
814
+ const oldData = newData;
815
+ newData = {};
816
+ for (const [key, value] of Object.entries(oldData)) {
817
+ /** @type {FieldDefine & {group?: string}?} */
818
+ const field = fields[key];
819
+ if (!field) continue;
820
+ if (!groups.has(field.group || "") && !fieldsAuth.has(key)) continue;
821
+ newData[key] = value;
822
+ }
823
+ const newPermissionGroups = await filterPermissions(permissions, {
824
+ ...document,
825
+ ...newData
826
+ });
827
+ if (newPermissionGroups && !newPermissionGroups.length) return null;
828
+ const newGroups = new Set(newPermissionGroups.flatMap((v) => v.group || ""));
829
+ const newFieldsAuth = new Set(newPermissionGroups.flatMap((v) => v.fields || ""));
830
+ newGroups.delete("");
831
+ newFieldsAuth.delete("");
832
+ if (newGroups.size === groups.size && newFieldsAuth.size === fieldsAuth.size) break;
833
+ groups = newGroups;
834
+ fieldsAuth = newFieldsAuth;
835
+ }
836
+ if (!(groups.size + fieldsAuth.size)) return null;
837
+ /**
838
+ *
839
+ * @param {[string, FieldDefine & {group?: string}][]} fields
840
+ * @param {Record<string, any>} document
841
+ * @param {string[]} parent
842
+ * @returns
843
+ */
844
+ function filter(fields, document, parent) {
845
+ /** @type {Record<string, any>} */
846
+ const value = {};
847
+ for (const [name, field] of fields) {
848
+ if (!field) continue;
849
+ const fieldNames = [...parent, name];
850
+ const key = fieldNames.join(".");
851
+ if ((parent ? !field.primary : true) && !groups.has(field.group || "") && !fieldsAuth.has(key) && !fieldsAuth.has("*")) continue;
852
+ if (!Object.hasOwn(document, name)) continue;
853
+ const { type } = field;
854
+ if (!type || typeof type !== "object" || !type.table) {
855
+ value[name] = document[name];
856
+ continue;
857
+ }
858
+ const subDocument = document[name];
859
+ const list = Object.entries(type.fields);
860
+ if (field.array) value[name] = (Array.isArray(subDocument) ? subDocument : [subDocument]).map((doc) => filter(list, doc, fieldNames));
861
+ else if (subDocument && !Array.isArray(subDocument)) {
862
+ const val = filter(list, subDocument, fieldNames);
863
+ if (!Object.keys(val)) continue;
864
+ value[name] = val;
865
+ }
866
+ }
867
+ return value;
868
+ }
869
+ return filter(Object.entries(fields), newDocument, []);
870
+ }
871
+
872
+ //#endregion
873
+ //#region packages/core/permission/testPermissions.mjs
874
+ /** @import { Permission } from '@yongdall/types' */
875
+ /**
876
+ *
877
+ * @param {Permission[]?} [allPermissions]
878
+ * @param {Record<string, any>} [document]
879
+ * @returns
880
+ */
881
+ async function testPermissions(allPermissions, document) {
882
+ if (await isSingleUser()) return true;
883
+ if (!allPermissions?.length) return false;
884
+ const permissionInGlobal = allPermissions.filter((v) => !v.organizationField);
885
+ const permissionInOrganization = allPermissions.filter((v) => v.organizationField);
886
+ if (!document) {
887
+ if (permissionInGlobal.length) {
888
+ const permissions = await getUserPermissions();
889
+ for (const { permission } of permissionInGlobal) {
890
+ if (permission && !permissions.has(permission)) continue;
891
+ return true;
892
+ }
893
+ }
894
+ if (permissionInOrganization.length) {
895
+ const permissions = await getUserOrganizationAllPermissions();
896
+ for (const { permission } of permissionInOrganization) {
897
+ if (permission && !permissions.has(permission)) continue;
898
+ return true;
899
+ }
900
+ }
901
+ return false;
902
+ }
903
+ const userId = await getUser();
904
+ if (permissionInGlobal.length) {
905
+ const permissions = await getUserPermissions();
906
+ for (const { permission, constraints } of permissionInGlobal) {
907
+ if (permission && !permissions.has(permission)) continue;
908
+ if (constraints && !testPermissionConstraints(constraints, document, userId)) continue;
909
+ return true;
910
+ }
911
+ }
912
+ if (permissionInOrganization.length) {
913
+ const permissions = await getUserOrganizationPermissions();
914
+ for (const { permission, organizationField, constraints } of permissionInOrganization) {
915
+ if (constraints && !testPermissionConstraints(constraints, document, userId)) continue;
916
+ const organizationId = document[organizationField || ""];
917
+ if (!organizationId) continue;
918
+ const organizationPermissions = permissions[organizationId];
919
+ if (!organizationPermissions) continue;
920
+ if (permission && !organizationPermissions.has(permission)) continue;
921
+ return true;
922
+ }
923
+ }
924
+ return false;
925
+ }
926
+
927
+ //#endregion
928
+ //#region packages/core/decorators/modelHook.mjs
929
+ /** @import { Hooks, ClassDecorator } from '@yongdall/model' */
930
+ /** @returns {{[K in keyof Hooks]: Record<string, Hooks[K][]>}} */
931
+ function loadHooks() {
932
+ /** @returns {{[K in keyof Hooks]: Record<string, Hooks[K][]>}} */
933
+ const modelHooks = Object.create(null);
934
+ for (const [, model, v] of hooks.get("modelHook").entriesObject()) for (const [name, fn] of Object.entries(v)) {
935
+ if (typeof fn !== "function") continue;
936
+ const list = modelHooks[name] ||= Object.create(null);
937
+ (list[model] ||= []).push(fn);
938
+ }
939
+ return modelHooks;
940
+ }
941
+ const modelHooks = loadHooks();
942
+ /**
943
+ *
944
+ * @param {Partial<Hooks>} hooks
945
+ * @param {...string | string[]} hookNames
946
+ * @returns {Hooks}
947
+ */
948
+ function bindModelHooks({ where, beforeCreateMany, afterCreateMany, beforeCreate, afterCreate, beforeUpdate, afterUpdate, beforeDelete, afterDelete }, ...hookNames) {
949
+ /** @type {string[]} */
950
+ const names = hookNames.flat(21);
951
+ return {
952
+ where: where || (() => {}),
953
+ async beforeCreateMany(conn, model, record) {
954
+ let data = record;
955
+ for (const name of names) for (const hook of modelHooks.beforeCreateMany[name] || []) {
956
+ if (typeof hook !== "function") continue;
957
+ data = await hook(conn, model, data) || data;
958
+ }
959
+ if (typeof beforeCreateMany === "function") data = await beforeCreateMany(conn, model, data) || data;
960
+ return data;
961
+ },
962
+ async afterCreateMany(conn, model, record) {
963
+ if (typeof afterCreateMany === "function") await afterCreateMany(conn, model, record);
964
+ for (const name of names) for (const hook of modelHooks.afterCreateMany[name] || []) {
965
+ if (typeof hook !== "function") continue;
966
+ await hook(conn, model, record);
967
+ }
968
+ },
969
+ async beforeCreate(conn, model, record) {
970
+ let data = record;
971
+ for (const name of names) for (const hook of modelHooks.beforeCreate[name] || []) {
972
+ if (typeof hook !== "function") continue;
973
+ data = await hook(conn, model, data) || data;
974
+ }
975
+ if (typeof beforeCreate === "function") data = await beforeCreate(conn, model, data) || data;
976
+ return data;
977
+ },
978
+ async afterCreate(conn, model, record) {
979
+ if (typeof afterCreate === "function") await afterCreate(conn, model, record);
980
+ for (const name of names) for (const hook of modelHooks.afterCreate[name] || []) {
981
+ if (typeof hook !== "function") continue;
982
+ await hook(conn, model, record);
983
+ }
984
+ },
985
+ async beforeUpdate(conn, model, record, set) {
986
+ let data = set;
987
+ for (const name of names) for (const hook of modelHooks.beforeUpdate[name] || []) {
988
+ if (typeof hook !== "function") continue;
989
+ data = await hook(conn, model, record, data) || data;
990
+ }
991
+ if (typeof beforeUpdate === "function") data = await beforeUpdate(conn, model, record, data) || data;
992
+ return data;
993
+ },
994
+ async afterUpdate(conn, model, record, oldRecord) {
995
+ if (typeof afterUpdate === "function") await afterUpdate(conn, model, record, oldRecord);
996
+ for (const name of names) for (const hook of modelHooks.afterUpdate[name] || []) {
997
+ if (typeof hook !== "function") continue;
998
+ await hook(conn, model, record, oldRecord);
999
+ }
1000
+ },
1001
+ async beforeDelete(conn, model, record, update) {
1002
+ for (const name of names) for (const hook of modelHooks.beforeDelete[name] || []) {
1003
+ if (typeof hook !== "function") continue;
1004
+ await hook(conn, model, record, update);
1005
+ }
1006
+ if (typeof beforeDelete === "function") await beforeDelete(conn, model, record, update);
1007
+ },
1008
+ async afterDelete(conn, model, record, update) {
1009
+ if (typeof afterDelete === "function") await afterDelete(conn, model, record, update);
1010
+ for (const name of names) for (const hook of modelHooks.afterDelete[name] || []) {
1011
+ if (typeof hook !== "function") continue;
1012
+ await hook(conn, model, record, update);
1013
+ }
1014
+ }
1015
+ };
1016
+ }
1017
+ /**
1018
+ *
1019
+ * @param {...string | string[]} hookNames
1020
+ * @returns {ClassDecorator}
1021
+ */
1022
+ function modelHook(...hookNames) {
1023
+ /** @type {string[]} */
1024
+ const names = hookNames.flat(21);
1025
+ return (Model, ctx) => {
1026
+ if (!names.length) return;
1027
+ beforeCreateMany(Model, async (conn, model, record) => {
1028
+ let data = record;
1029
+ for (const name of names) for (const hook of modelHooks.beforeCreateMany[name] || []) {
1030
+ if (typeof hook !== "function") continue;
1031
+ data = await hook(conn, model, data) || data;
1032
+ }
1033
+ return data;
1034
+ });
1035
+ afterCreateMany(Model, async (conn, model, record) => {
1036
+ for (const name of names) for (const hook of modelHooks.afterCreateMany[name] || []) {
1037
+ if (typeof hook !== "function") continue;
1038
+ await hook(conn, model, record);
1039
+ }
1040
+ });
1041
+ beforeCreate(Model, async (conn, model, record) => {
1042
+ let data = record;
1043
+ for (const name of names) for (const hook of modelHooks.beforeCreate[name] || []) {
1044
+ if (typeof hook !== "function") continue;
1045
+ data = await hook(conn, model, data) || data;
1046
+ }
1047
+ return data;
1048
+ });
1049
+ afterCreate(Model, async (conn, model, record) => {
1050
+ for (const name of names) for (const hook of modelHooks.afterCreate[name] || []) {
1051
+ if (typeof hook !== "function") continue;
1052
+ await hook(conn, model, record);
1053
+ }
1054
+ });
1055
+ beforeUpdate(Model, async (conn, model, record, set) => {
1056
+ let data = set;
1057
+ for (const name of names) for (const hook of modelHooks.beforeUpdate[name] || []) {
1058
+ if (typeof hook !== "function") continue;
1059
+ data = await hook(conn, model, record, data) || data;
1060
+ }
1061
+ return data;
1062
+ });
1063
+ afterUpdate(Model, async (conn, model, record, oldRecord) => {
1064
+ for (const name of names) for (const hook of modelHooks.afterUpdate[name] || []) {
1065
+ if (typeof hook !== "function") continue;
1066
+ await hook(conn, model, record, oldRecord);
1067
+ }
1068
+ });
1069
+ beforeDelete(Model, async (conn, model, record, update) => {
1070
+ for (const name of names) for (const hook of modelHooks.beforeDelete[name] || []) {
1071
+ if (typeof hook !== "function") continue;
1072
+ await hook(conn, model, record, update);
1073
+ }
1074
+ });
1075
+ afterDelete(Model, async (conn, model, record, update) => {
1076
+ for (const name of names) for (const hook of modelHooks.afterDelete[name] || []) {
1077
+ if (typeof hook !== "function") continue;
1078
+ await hook(conn, model, record, update);
1079
+ }
1080
+ });
1081
+ };
1082
+ }
1083
+
1084
+ //#endregion
1085
+ //#region packages/core/ServerError.mjs
1086
+ var ServerError = class extends Error {
1087
+ status = 500;
1088
+ /**
1089
+ *
1090
+ * @param {string} message
1091
+ * @param {number} [status]
1092
+ */
1093
+ constructor(message, status = 500) {
1094
+ super(message);
1095
+ this.status = status;
1096
+ }
1097
+ };
1098
+
1099
+ //#endregion
1100
+ //#region packages/core/decorators/nestedSetTree.mjs
1101
+ /**
1102
+ *
1103
+ * @param {Connection} conn
1104
+ * @param {any} Model
1105
+ * @param {number} id
1106
+ * @param {number} lftValue
1107
+ * @param {number} rgtValue
1108
+ * @param {string} idField
1109
+ * @param {string} lftField
1110
+ * @param {string} rgtField
1111
+ */
1112
+ async function treeValidateLoop(conn, Model, id, lftValue, rgtValue, idField, lftField, rgtField) {
1113
+ if ((await conn.select(new Query(Model).select(idField).where(lftField, "<=", lftValue).where(rgtField, ">=", rgtValue).where({ [idField]: id }).limit(1))).length) throw new ServerError("项目不能被添加到自己的后代");
1114
+ }
1115
+ /**
1116
+ *
1117
+ * @param {string} parentField
1118
+ * @param {string} lftField
1119
+ * @param {string} rgtField
1120
+ * @param {string} idField
1121
+ * @param {...string} groupFields
1122
+ */
1123
+ function nestedSetTree(parentField, lftField = "lft", rgtField = "rgt", idField = "id", ...groupFields) {
1124
+ /**
1125
+ *
1126
+ * @param {any} Model
1127
+ * @param {ClassDecoratorContext} ctx
1128
+ */
1129
+ return function(Model, ctx) {
1130
+ beforeCreate(Model, async (conn, record) => {
1131
+ let begin = 1;
1132
+ const parent = record[parentField];
1133
+ const where = new Where();
1134
+ for (const g of groupFields) where.and(g, record[g]);
1135
+ if (parent) {
1136
+ const [{ [rgtField]: rgtValue }] = await conn.select(new Query(Model, true).select(rgtField).where(where).where({ [idField]: parent }).limit(1));
1137
+ begin = rgtValue;
1138
+ await conn.update(new Query(Model).where(where).where(rgtField, ">=", begin), { [rgtField]: increment(2) });
1139
+ await conn.update(new Query(Model).where(where).where(lftField, ">=", begin), { [lftField]: increment(2) });
1140
+ } else {
1141
+ /** @type {{r: number}[]} */
1142
+ const [{ r } = { r: 0 }] = await conn.select(new Query(Model, true).select({ r: rgtField }).where(where).where({ [idField]: parent }).limit(1).sort(rgtField, true));
1143
+ begin = r + 1;
1144
+ }
1145
+ record[lftField] = begin;
1146
+ record[rgtField] = begin + 1;
1147
+ }, true);
1148
+ beforeUpdate(Model, async (conn, oldDocument, updatedData) => {
1149
+ const parentId = parentField in updatedData ? updatedData[parentField] : oldDocument[parentField];
1150
+ if (parentId === oldDocument[parentField]) {
1151
+ updatedData[lftField] = oldDocument[lftField];
1152
+ updatedData[rgtField] = oldDocument[rgtField];
1153
+ return;
1154
+ }
1155
+ const where = new Where();
1156
+ for (const g of groupFields) where.and(g, updatedData[g] ?? oldDocument[g]);
1157
+ if (parentId) {
1158
+ const [parent] = await conn.select(new Query(Model).select({
1159
+ lft: lftField,
1160
+ rgt: rgtField
1161
+ }).where(where).where({ [idField]: parentId }));
1162
+ const { lft, rgt } = parent;
1163
+ await treeValidateLoop(conn, Model, oldDocument[idField], lft, rgt, idField, lftField, rgtField);
1164
+ }
1165
+ await conn.update(new Query(Model).where(where).where(lftField, ">=", oldDocument[rgtField]).where(rgtField, "<=", oldDocument[rgtField]), {
1166
+ [rgtField]: multiply(-1),
1167
+ [rgtField]: multiply(-1)
1168
+ });
1169
+ const diff = oldDocument[rgtField] - oldDocument[lftField] + 1;
1170
+ await conn.update(new Query(Model).where(where).where(lftField, ">", oldDocument[rgtField]), { [lftField]: decrement(diff) });
1171
+ await conn.update(new Query(Model).where(where).where(rgtField, ">", oldDocument[rgtField]), { [rgtField]: decrement(diff) });
1172
+ let newLft = 0;
1173
+ if (parentId) {
1174
+ const [{ [rgtField]: rgtValue }] = await conn.select(new Query(Model).select(rgtField).where(where).where({ [idField]: parentId }));
1175
+ await conn.update(new Query(Model).where(where).where(rgtField, ">=", rgtValue), { [rgtField]: increment(diff) });
1176
+ await conn.update(new Query(Model).where(where).where(lftField, ">=", rgtValue), { [lftField]: increment(diff) });
1177
+ newLft = rgtValue;
1178
+ } else {
1179
+ /** @type {{r: number}[]} */
1180
+ const [{ r } = { r: 0 }] = await conn.select(new Query(Model, true).select({ r: rgtField }).where(where).where({ [idField]: parent }).limit(1).sort(rgtField, true));
1181
+ newLft = r + 1;
1182
+ }
1183
+ const val = newLft - oldDocument[lftField];
1184
+ await conn.update(new Query(Model).where(where).where(lftField, "<", 0), {
1185
+ [lftField]: coefficient(val, -1),
1186
+ [rgtField]: coefficient(val, -1)
1187
+ });
1188
+ updatedData[lftField] = oldDocument[lftField] + val;
1189
+ updatedData[rgtField] = oldDocument[rgtField] + val;
1190
+ }, true);
1191
+ };
1192
+ }
1193
+
1194
+ //#endregion
1195
+ //#region packages/core/toDocumentFields.mjs
1196
+ /** @import { Field } from '@yongdall/types' */
1197
+ /** @import { FieldDefine } from '@yongdall/model' */
1198
+ /**
1199
+ *
1200
+ * @param {Record<string, FieldDefine>} fields
1201
+ * @param {Set<string>} ignore
1202
+ * @returns {Record<string, Field>?}
1203
+ */
1204
+ function toSubDocumentFields(fields, ignore) {
1205
+ /** @type {Record<string, Field>} */
1206
+ const res = {};
1207
+ for (const [name, { type, ...field }] of Object.entries(fields)) {
1208
+ if (ignore.has(name)) continue;
1209
+ if (!type) continue;
1210
+ if (typeof type !== "object" && typeof type !== "function") {
1211
+ res[name] = {
1212
+ type,
1213
+ array: field.array,
1214
+ nullable: field.nullable,
1215
+ default: field.default,
1216
+ scale: field.scale,
1217
+ primary: field.primary,
1218
+ uncreatable: Boolean(field.uncreatable || field.updating || field.creating || field.fixedValue),
1219
+ immutable: Boolean(field.primary || field.immutable || field.updating || field.creating || field.fixedValue),
1220
+ layoutOrder: field.layoutOrder,
1221
+ label: field.label,
1222
+ description: field.description,
1223
+ placeholder: field.placeholder,
1224
+ step: field.step,
1225
+ max: field.max,
1226
+ min: field.min,
1227
+ readonly: field.readonly,
1228
+ required: field.required,
1229
+ clearable: field.clearable,
1230
+ disabled: field.disabled,
1231
+ group: field.group,
1232
+ renderer: field.renderer,
1233
+ scripts: field.scripts,
1234
+ layout: field.layout,
1235
+ model: field.model,
1236
+ sort: field.sort
1237
+ };
1238
+ continue;
1239
+ }
1240
+ const fields = type.fields;
1241
+ if (!fields) continue;
1242
+ const subFields = toSubDocumentFields(fields, new Set(Object.keys(field.constraints || {})));
1243
+ if (!subFields) continue;
1244
+ res[name] = {
1245
+ type: subFields,
1246
+ array: field.array,
1247
+ default: field.default,
1248
+ uncreatable: Boolean(field.uncreatable),
1249
+ immutable: Boolean(field.immutable),
1250
+ layoutOrder: field.layoutOrder,
1251
+ label: field.label,
1252
+ description: field.description,
1253
+ placeholder: field.placeholder,
1254
+ readonly: field.readonly,
1255
+ required: field.required,
1256
+ clearable: field.clearable,
1257
+ disabled: field.disabled,
1258
+ group: field.group,
1259
+ renderer: field.renderer,
1260
+ scripts: field.scripts,
1261
+ layout: field.layout
1262
+ };
1263
+ }
1264
+ if (!Object.keys(res).length) return null;
1265
+ return res;
1266
+ }
1267
+ /**
1268
+ *
1269
+ * @param {Record<string, FieldDefine>} fields
1270
+ * @returns {Record<string, Field>}
1271
+ */
1272
+ function toDocumentFields(fields) {
1273
+ return toSubDocumentFields(fields, /* @__PURE__ */ new Set()) || {};
1274
+ }
1275
+
1276
+ //#endregion
1277
+ //#region packages/core/enumerations.mjs
1278
+ /** @import { MaybePromise } from './types.mjs' */
1279
+ /** @type {Record<string, (Record<string, any> | (() => MaybePromise<Record<string, any>[]>))[]>} */
1280
+ let enumerations = {};
1281
+ hooks.listen(() => {
1282
+ const groups = Object.groupBy([...hooks.get("enumerations").values()].flatMap((v) => Object.entries(v)), ([k]) => k);
1283
+ enumerations = Object.fromEntries(Object.entries(groups).map(([k, v]) => [k, v?.map((v) => v[1]).flat(2) || []]));
1284
+ }, -1e6);
1285
+ /**
1286
+ *
1287
+ * @param {string} name
1288
+ * @returns {Promise<Record<string, any>[]>}
1289
+ */
1290
+ async function getEnumerations(name) {
1291
+ if (!Object.hasOwn(enumerations, name)) return [];
1292
+ return (await Promise.all(enumerations[name].map((v) => typeof v === "function" ? v() : v))).flat();
1293
+ }
1294
+
1295
+ //#endregion
1296
+ //#region packages/core/search2where/hook.mjs
1297
+ /** @import { MaybePromise, WhereHook } from '../types.mjs' */
1298
+ /**
1299
+ *
1300
+ * @param {MaybePromise<Where?>} [where]
1301
+ */
1302
+ async function toNot(where) {
1303
+ const w = await where;
1304
+ if (!w) return null;
1305
+ return Where.not(w);
1306
+ }
1307
+ /**
1308
+ * @template T
1309
+ * @param {Record<string, T>} map
1310
+ * @param {string} key
1311
+ * @param {() => T} create
1312
+ */
1313
+ function select(map, key, create) {
1314
+ if (Object.hasOwn(map, key)) return map[key];
1315
+ return map[key] = create();
1316
+ }
1317
+ /**
1318
+ * @typedef WhereDefine
1319
+ * @property {string} label
1320
+ * @property {string} notLabel
1321
+ * @property {WhereHook.Value[]} value
1322
+ * @property {WhereHook.Value[]} notValue
1323
+ * @property {WhereHook.Named[]} named
1324
+ * @property {WhereHook.Named[]} notNamed
1325
+ * @property {WhereHook.Field[]} field
1326
+ * @property {WhereHook.Field[]} notField
1327
+ */
1328
+ /**
1329
+ *
1330
+ * @returns {WhereDefine}
1331
+ */
1332
+ function createWhereDefine() {
1333
+ return {
1334
+ label: "",
1335
+ notLabel: "",
1336
+ value: [],
1337
+ notValue: [],
1338
+ named: [],
1339
+ notNamed: [],
1340
+ field: [],
1341
+ notField: []
1342
+ };
1343
+ }
1344
+ function getWhere() {
1345
+ /** @type {Record<string, Record<string, WhereDefine>>} */
1346
+ const filterMap = {};
1347
+ /** @type {Record<string, Record<string, WhereDefine>>} */
1348
+ const notFilterMap = {};
1349
+ for (const [, type, fieldWhere] of hooks.get("where").entriesObject()) {
1350
+ if (!fieldWhere || typeof fieldWhere !== "object") continue;
1351
+ const typeFilters = select(filterMap, type, () => ({}));
1352
+ const notTypeFilters = select(notFilterMap, type, () => ({}));
1353
+ for (const [op, filters] of Object.entries(fieldWhere)) for (const filter of [filters].flat()) {
1354
+ if (!filter || typeof filter !== "object") continue;
1355
+ let { label, notLabel, not, value, notValue, named, notNamed, field, notField } = filter;
1356
+ if (not) {
1357
+ if (typeof value === "function" && true) notValue = (...p) => toNot(value(...p));
1358
+ if (typeof field === "function" && true) notField = (...p) => toNot(field(...p));
1359
+ if (typeof named === "function" && true) notNamed = (...p) => toNot(named(...p));
1360
+ }
1361
+ const typeFilter = select(typeFilters, op, createWhereDefine);
1362
+ if (label && typeof label === "string") typeFilter.label = label;
1363
+ if (notLabel && typeof notLabel === "string") typeFilter.notLabel = notLabel;
1364
+ if (typeof value === "function") typeFilter.value.push(value);
1365
+ if (typeof notValue === "function") typeFilter.notValue.push(notValue);
1366
+ if (typeof named === "function") typeFilter.named.push(named);
1367
+ if (typeof notNamed === "function") typeFilter.notNamed.push(notNamed);
1368
+ if (typeof field === "function") typeFilter.field.push(field);
1369
+ if (typeof notField === "function") typeFilter.notField.push(notField);
1370
+ const notOp = typeof not === "string" && not || "";
1371
+ if (!notOp) continue;
1372
+ const notTypeFilter = select(notTypeFilters, notOp, createWhereDefine);
1373
+ if (label && typeof label === "string") notTypeFilter.notLabel = label;
1374
+ if (notLabel && typeof notLabel === "string") notTypeFilter.label = notLabel;
1375
+ if (typeof value === "function") notTypeFilter.notValue.push(value);
1376
+ if (typeof notValue === "function") notTypeFilter.value.push(notValue);
1377
+ if (typeof named === "function") notTypeFilter.notNamed.push(named);
1378
+ if (typeof notNamed === "function") notTypeFilter.named.push(notNamed);
1379
+ if (typeof field === "function") notTypeFilter.notField.push(field);
1380
+ if (typeof notField === "function") notTypeFilter.field.push(notField);
1381
+ }
1382
+ }
1383
+ for (const type of Object.keys({
1384
+ ...filterMap,
1385
+ ...notFilterMap
1386
+ })) {
1387
+ let typeFilters = Object.hasOwn(filterMap, type) && filterMap[type] || null;
1388
+ let notTypeFilters = Object.hasOwn(notFilterMap, type) && notFilterMap[type] || null;
1389
+ if (!typeFilters && notTypeFilters) {
1390
+ typeFilters = notTypeFilters;
1391
+ filterMap[type] = typeFilters;
1392
+ }
1393
+ if (!typeFilters) continue;
1394
+ for (const op of Object.keys({
1395
+ ...typeFilters,
1396
+ ...notTypeFilters
1397
+ })) {
1398
+ let opFilters = Object.hasOwn(typeFilters, op) && typeFilters[op] || null;
1399
+ let notOpFilters = notTypeFilters && Object.hasOwn(notTypeFilters, op) && notTypeFilters[op] || null;
1400
+ if (!opFilters && notOpFilters) {
1401
+ opFilters = notOpFilters;
1402
+ typeFilters[type] = opFilters;
1403
+ }
1404
+ if (!opFilters) continue;
1405
+ if (!opFilters.label) opFilters.label = notOpFilters?.label || op;
1406
+ if (!opFilters.notLabel) opFilters.notLabel = notOpFilters?.notLabel || `!${op}`;
1407
+ if (!notOpFilters) continue;
1408
+ for (const fn of notOpFilters.value) opFilters.value.push(fn);
1409
+ for (const fn of notOpFilters.notValue) opFilters.notValue.push(fn);
1410
+ for (const fn of notOpFilters.named) opFilters.named.push(fn);
1411
+ for (const fn of notOpFilters.notNamed) opFilters.notNamed.push(fn);
1412
+ for (const fn of notOpFilters.field) opFilters.field.push(fn);
1413
+ for (const fn of notOpFilters.notField) opFilters.notField.push(fn);
1414
+ }
1415
+ }
1416
+ return filterMap;
1417
+ }
1418
+ /**
1419
+ *
1420
+ * @param {ReturnType<typeof getWhere>} filterMap
1421
+ */
1422
+ function toWhereList(filterMap) {
1423
+ /** @type {{type: string; operator: string; label: string; notLabel: string; value: boolean; notValue: boolean; named: boolean; notNamed: boolean; field: boolean; notField: boolean}[]} */
1424
+ const list = [];
1425
+ for (const [type, filters] of Object.entries(filterMap)) for (const [operator, filter] of Object.entries(filters)) list.push({
1426
+ type,
1427
+ operator,
1428
+ label: filter.label,
1429
+ notLabel: filter.notLabel,
1430
+ value: Boolean(filter.value.length),
1431
+ notValue: Boolean(filter.notValue.length),
1432
+ named: Boolean(filter.named.length),
1433
+ notNamed: Boolean(filter.notNamed.length),
1434
+ field: Boolean(filter.field.length),
1435
+ notField: Boolean(filter.notField.length)
1436
+ });
1437
+ }
1438
+ let allWhere = getWhere();
1439
+ let whereList = toWhereList(allWhere);
1440
+ hooks.listen(() => {
1441
+ allWhere = getWhere();
1442
+ whereList = toWhereList(allWhere);
1443
+ });
1444
+
1445
+ //#endregion
1446
+ //#region packages/core/search2where/toWhere.mjs
1447
+ /** @import { FieldDefine } from '@yongdall/model' */
1448
+ /** @import { MaybePromise } from '../types.mjs' */
1449
+ /**
1450
+ * @typedef {object} WhereParam.Value
1451
+ * @property {'value'} type
1452
+ * @property {FieldDefine} define
1453
+ * @property {string} name
1454
+ * @property {string?} operator
1455
+ * @property {boolean} not
1456
+ * @property {string | RecursiveArray<string> | null} value
1457
+ */
1458
+ /**
1459
+ * @typedef {object} WhereParam.Field
1460
+ * @property {'field'} type
1461
+ * @property {FieldDefine} define
1462
+ * @property {string} name
1463
+ * @property {FieldDefine} fieldDefine
1464
+ * @property {string} field
1465
+ * @property {string?} operator
1466
+ * @property {boolean} not
1467
+ */
1468
+ /**
1469
+ * @typedef {WhereParam.Value | WhereParam.Field} WhereParam
1470
+ */
1471
+ /**
1472
+ *
1473
+ * @param {Record<string, FieldDefine>} fields
1474
+ * @param {string[]} path
1475
+ * @param {Search.Match | Search.Children} and
1476
+ * @returns {Generator<WhereParam, Where?, Where?>}
1477
+ */
1478
+ function* runItemWhere(fields, path, and) {
1479
+ const name = path.shift();
1480
+ if (!name) return null;
1481
+ const define = Object.hasOwn(fields, name) && fields[name];
1482
+ if (!define) return null;
1483
+ const type = define.type;
1484
+ if (!type) return null;
1485
+ if (path.length) {
1486
+ if (typeof type !== "object") return null;
1487
+ if (!type.table) return null;
1488
+ return null;
1489
+ }
1490
+ if (and.length === 2) {
1491
+ if (typeof type !== "object") return null;
1492
+ if (!type.table) return null;
1493
+ return null;
1494
+ }
1495
+ if (typeof type !== "string" && (typeof type !== "object" || type.table)) return null;
1496
+ const [, value, operator, sign] = and;
1497
+ if (operator === null) return yield {
1498
+ type: "value",
1499
+ define,
1500
+ name: define.column || name,
1501
+ value: null,
1502
+ operator,
1503
+ not: Boolean(sign)
1504
+ };
1505
+ if (!value || typeof value !== "object" || Array.isArray(value)) return yield {
1506
+ type: "value",
1507
+ define,
1508
+ name: define.column || name,
1509
+ value,
1510
+ operator,
1511
+ not: Boolean(sign)
1512
+ };
1513
+ const { field, table, value: rValue } = value;
1514
+ if (rValue) return yield {
1515
+ type: "value",
1516
+ define,
1517
+ name: define.column || name,
1518
+ value: rValue,
1519
+ operator,
1520
+ not: Boolean(sign)
1521
+ };
1522
+ if (field) {
1523
+ if (table) return null;
1524
+ const fieldDefine = Object.hasOwn(fields, name) && fields[field];
1525
+ if (!fieldDefine) return null;
1526
+ const type = fieldDefine.type;
1527
+ if (!type) return null;
1528
+ if (typeof type !== "string" && (typeof type !== "object" || type.table)) return null;
1529
+ return yield {
1530
+ type: "field",
1531
+ define,
1532
+ name: define.column || name,
1533
+ field: fieldDefine.column || field,
1534
+ fieldDefine,
1535
+ operator,
1536
+ not: Boolean(sign)
1537
+ };
1538
+ }
1539
+ return null;
1540
+ }
1541
+ /**
1542
+ *
1543
+ * @param {Record<string, FieldDefine>} fields
1544
+ * @param {Search.Match | Search.Children} and
1545
+ * @returns {Generator<WhereParam, Where?, Where?>}
1546
+ */
1547
+ function* toItemWhere(fields, and) {
1548
+ const path = and[0];
1549
+ if (path.find((v) => Array.isArray(v) && v.length)) return null;
1550
+ return yield* runItemWhere(fields, path.map((v) => typeof v === "string" ? v : v[0]), and);
1551
+ }
1552
+ /**
1553
+ *
1554
+ * @param {Record<string, FieldDefine>} fields
1555
+ * @param {Search.OrList} or
1556
+ * @returns {Generator<WhereParam, Where?, Where?>}
1557
+ */
1558
+ function* toOrWhere(fields, or) {
1559
+ /** @type {Where?} */
1560
+ let where = null;
1561
+ for (const item of or) if (Array.isArray(item)) {
1562
+ const w = yield* toItemWhere(fields, item);
1563
+ if (w) where = (where || new Where()).or(w);
1564
+ } else {
1565
+ const w = yield* toWhere(fields, item.and, item.or);
1566
+ if (w) where = (where || new Where()).or(w);
1567
+ }
1568
+ return where;
1569
+ }
1570
+ /**
1571
+ *
1572
+ * @param {Record<string, FieldDefine>} fields
1573
+ * @param {Search.AndList} [and]
1574
+ * @param {...Search.OrList | null | undefined} orList
1575
+ * @returns {Generator<WhereParam, Where?, Where?>}
1576
+ */
1577
+ function* toWhere(fields, and, ...orList) {
1578
+ /** @type {Where?} */
1579
+ let where = null;
1580
+ if (and) for (const item of and) {
1581
+ const w = yield* toItemWhere(fields, item);
1582
+ if (w) where = (where || new Where()).and(w);
1583
+ }
1584
+ for (const or of orList) {
1585
+ if (!or) continue;
1586
+ const w = yield* toOrWhere(fields, or);
1587
+ if (w) where = (where || new Where()).and(w);
1588
+ }
1589
+ return where;
1590
+ }
1591
+
1592
+ //#endregion
1593
+ //#region packages/core/search2where/index.mjs
1594
+ /** @import { RecursiveArray } from '@yongdall/common' */
1595
+ /** @import { FieldDefine } from '@yongdall/model' */
1596
+ /** @import { MaybePromise } from '../types.mjs' */
1597
+ /** @import { WhereParam } from './toWhere.mjs' */
1598
+ /**
1599
+ *
1600
+ * @param {string | {component: string; [k: string]: any} | (string | {component: string; [k: string]: any})[]} [def]
1601
+ * @returns {Iterable<string>}
1602
+ */
1603
+ function* getRenderer(def) {
1604
+ if (!def) return;
1605
+ for (const s of Array.isArray(def) ? def : [def]) {
1606
+ if (!s) continue;
1607
+ if (typeof s === "string") {
1608
+ yield s;
1609
+ continue;
1610
+ }
1611
+ if (typeof s !== "object") continue;
1612
+ const component = s.component;
1613
+ if (!component || typeof component !== "string") continue;
1614
+ yield component;
1615
+ }
1616
+ }
1617
+ /**
1618
+ *
1619
+ * @param {FieldDefine} field
1620
+ * @returns {Iterable<string>}
1621
+ */
1622
+ function* getKey(field) {
1623
+ const type = field.type;
1624
+ const renderers = field.renderer;
1625
+ const array = field.array ? "[]" : "";
1626
+ if (typeof type === "object") return;
1627
+ if (!type) {
1628
+ for (const renderer of getRenderer(renderers)) {
1629
+ yield `:${renderer}${array}`;
1630
+ yield `${renderer}${array}`;
1631
+ }
1632
+ yield `${array}`;
1633
+ return;
1634
+ }
1635
+ for (const renderer of getRenderer(renderers)) {
1636
+ yield `${type}:${renderer}${array}`;
1637
+ yield `${renderer}${array}`;
1638
+ }
1639
+ yield `${type}${array}`;
1640
+ yield `${array}`;
1641
+ }
1642
+ /**
1643
+ *
1644
+ * @param {WhereParam} param
1645
+ */
1646
+ async function execWhere(param) {
1647
+ const operator = param.operator;
1648
+ if (operator === null) return Where.and(param.name, "=", null, param.not);
1649
+ for (const k of getKey(param.define)) {
1650
+ const typeWhere = Object.hasOwn(allWhere, k) && allWhere[k];
1651
+ if (!typeWhere) continue;
1652
+ const op = Object.hasOwn(typeWhere, operator) && typeWhere[operator];
1653
+ if (!op) continue;
1654
+ if (param.type === "value") {
1655
+ for (const fn of param.not ? op.notValue : op.value) {
1656
+ const w = await fn(param.name, param.value, param.define);
1657
+ if (w) return w;
1658
+ }
1659
+ continue;
1660
+ }
1661
+ if (param.type === "field") {
1662
+ for (const fn of param.not ? op.notField : op.field) {
1663
+ const w = await fn(param.name, param.field, param.define, param.fieldDefine);
1664
+ if (w) return w;
1665
+ }
1666
+ continue;
1667
+ }
1668
+ }
1669
+ if (param.type === "field") for (const k of getKey(param.fieldDefine)) {
1670
+ const typeWhere = Object.hasOwn(allWhere, k) && allWhere[k];
1671
+ if (!typeWhere) continue;
1672
+ const op = Object.hasOwn(typeWhere, operator) && typeWhere[operator];
1673
+ if (!op) continue;
1674
+ for (const fn of param.not ? op.notField : op.field) {
1675
+ const w = await fn(param.field, param.name, param.fieldDefine, param.define);
1676
+ if (w) return w;
1677
+ }
1678
+ }
1679
+ return null;
1680
+ }
1681
+ /**
1682
+ *
1683
+ * @param {Record<string, FieldDefine>} fields
1684
+ * @param {Search.AndList} [and]
1685
+ * @param {Search.OrList} [or]
1686
+ * @param {Search.OrList} [or2]
1687
+ * @returns {Promise<Where?>}
1688
+ */
1689
+ async function search2where(fields, and, or, or2) {
1690
+ return Search.asyncRunGenerator(toWhere(fields, and, or, or2), execWhere, null);
1691
+ }
1692
+
1693
+ //#endregion
1694
+ //#region packages/core/index.mjs
1695
+ /**
1696
+ *
1697
+ * @param {Partial<Omit<Tenant, 'id'>>} tenant
1698
+ * @param {object} boot
1699
+ * @param {Record<string, Partial<Hooks>>} pluginHooks
1700
+ * @returns
1701
+ */
1702
+ function initSystem(tenant, boot, pluginHooks) {
1703
+ initConfig(tenant, boot);
1704
+ Hook.update(hooks, pluginHooks);
1705
+ }
1706
+
1707
+ //#endregion
1708
+ export { ServerError, bindModelHooks, bootConfiguration, createPermissionMatches, initSystem as default, existUser, filterPermissionDocument, filterPermissionUpdate, filterPermissions, findModel, findUser, getEnumerations, getMenus, getModel, getModelAuthorizationPermissions, getModelPermissions, getUser, getUserOrganizationAllPermissions, getUserOrganizationPermissions, getUserPermissions, hooks, initConfig, isSingleUser, loadPluginUser, loadUser, loadUserOrganizationAllPermissions, loadUserOrganizationPermissions, loadUserPermissions, mainTenant, modelHook, modelLoaders, models, nestedSetTree, search2where, setUser, setUserVerify, testPermissionConstraints, testPermissions, toDocumentFields, useTenant, verifyUser };
1709
+ //# sourceMappingURL=index.mjs.map