@yongdall/common 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 +1158 -0
- package/index.mjs.map +1 -0
- package/package.json +12 -0
- package/types.d.mts +391 -0
package/index.mjs
ADDED
|
@@ -0,0 +1,1158 @@
|
|
|
1
|
+
//#region \0rolldown/runtime.js
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __exportAll = (all, no_symbols) => {
|
|
4
|
+
let target = {};
|
|
5
|
+
for (var name in all) {
|
|
6
|
+
__defProp(target, name, {
|
|
7
|
+
get: all[name],
|
|
8
|
+
enumerable: true
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
if (!no_symbols) {
|
|
12
|
+
__defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
13
|
+
}
|
|
14
|
+
return target;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region packages/common/Search/util.mjs
|
|
19
|
+
/**
|
|
20
|
+
* @template T
|
|
21
|
+
* @template TReturn
|
|
22
|
+
* @template TNext
|
|
23
|
+
* @param {Generator<T, TReturn, TNext>} generator
|
|
24
|
+
* @param {(value: T) => TNext} run
|
|
25
|
+
* @param {TNext} def
|
|
26
|
+
*/
|
|
27
|
+
function runGenerator(generator, run, def) {
|
|
28
|
+
/** @type {TNext} */
|
|
29
|
+
let value = def;
|
|
30
|
+
for (;;) {
|
|
31
|
+
const result = generator.next(value);
|
|
32
|
+
if (result.done) return result.value;
|
|
33
|
+
value = run(result.value);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* @template T
|
|
38
|
+
* @template TReturn
|
|
39
|
+
* @template TNext
|
|
40
|
+
* @param {Generator<T, TReturn, TNext>} generator
|
|
41
|
+
* @param {(value: T) => PromiseLike<TNext> | TNext} run
|
|
42
|
+
* @param {TNext} def
|
|
43
|
+
*/
|
|
44
|
+
async function asyncRunGenerator(generator, run, def) {
|
|
45
|
+
/** @type {TNext} */
|
|
46
|
+
let value = def;
|
|
47
|
+
for (;;) {
|
|
48
|
+
const result = generator.next(value);
|
|
49
|
+
if (result.done) return result.value;
|
|
50
|
+
value = await run(result.value);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
//#endregion
|
|
55
|
+
//#region packages/common/Search/exec.mjs
|
|
56
|
+
/** @import { Children, Field, Match, Or } from './types.mjs' */
|
|
57
|
+
/**
|
|
58
|
+
*
|
|
59
|
+
* @param {Record<string, any>[]} data
|
|
60
|
+
* @param {Field[]} fields
|
|
61
|
+
* @returns {any[]}
|
|
62
|
+
*/
|
|
63
|
+
function select(data, fields) {
|
|
64
|
+
let list = data;
|
|
65
|
+
for (let item of fields) {
|
|
66
|
+
const [field, ...sub] = typeof item === "string" ? [item] : item;
|
|
67
|
+
if (!list.length) continue;
|
|
68
|
+
/** @type {any[]} */
|
|
69
|
+
const subList = [];
|
|
70
|
+
it: for (const item of list) {
|
|
71
|
+
if (!item || typeof item !== "object") continue;
|
|
72
|
+
if (!Object.hasOwn(item, field)) continue;
|
|
73
|
+
let value = item[field];
|
|
74
|
+
for (const index of sub) {
|
|
75
|
+
if (!Array.isArray(value)) continue it;
|
|
76
|
+
value = value[index];
|
|
77
|
+
}
|
|
78
|
+
subList.push(value);
|
|
79
|
+
}
|
|
80
|
+
list = subList.flat(Infinity).filter((v) => v && typeof v === "object");
|
|
81
|
+
}
|
|
82
|
+
return list;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
*
|
|
86
|
+
* @param {Record<string, any>[]} list
|
|
87
|
+
* @param {string} field
|
|
88
|
+
* @returns {any[]}
|
|
89
|
+
*/
|
|
90
|
+
function selectValue(list, field) {
|
|
91
|
+
/** @type {any[]} */
|
|
92
|
+
const subList = [];
|
|
93
|
+
for (const item of list) {
|
|
94
|
+
if (!item || typeof item !== "object") continue;
|
|
95
|
+
if (!Object.hasOwn(item, field)) continue;
|
|
96
|
+
subList.push(item[field]);
|
|
97
|
+
}
|
|
98
|
+
return subList;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* @typedef {object} ExecParam
|
|
102
|
+
* @property {string[]} fields
|
|
103
|
+
* @property {string} operator
|
|
104
|
+
* @property {string?} [sign]
|
|
105
|
+
* @property {any} value
|
|
106
|
+
* @property {any[]} values
|
|
107
|
+
*/
|
|
108
|
+
/**
|
|
109
|
+
*
|
|
110
|
+
* @param {Record<string, any>[]} data
|
|
111
|
+
* @param {string[]} parentFields
|
|
112
|
+
* @param {Match} match
|
|
113
|
+
* @returns {Generator<ExecParam, boolean?, boolean?>}
|
|
114
|
+
*/
|
|
115
|
+
function* execMatch(data, parentFields, match) {
|
|
116
|
+
const fields = [...match[0]];
|
|
117
|
+
const currentFields = [...parentFields, ...fields.map((v) => typeof v === "string" ? v : v[0])];
|
|
118
|
+
const last = fields.pop();
|
|
119
|
+
if (!last) return null;
|
|
120
|
+
const lastField = typeof last === "string" ? last : last[0];
|
|
121
|
+
const records = select(data, fields);
|
|
122
|
+
if (!records.length) return false;
|
|
123
|
+
const [, value, operator, sign] = match;
|
|
124
|
+
if (operator === null) {
|
|
125
|
+
const values = selectValue(records, lastField);
|
|
126
|
+
return sign ? values.find((v) => v === null) : values.find((v) => v !== null);
|
|
127
|
+
}
|
|
128
|
+
if (typeof value !== "object" || !value || Array.isArray(value)) {
|
|
129
|
+
const values = selectValue(records, lastField);
|
|
130
|
+
if (!values.length) return false;
|
|
131
|
+
return yield {
|
|
132
|
+
fields: currentFields,
|
|
133
|
+
operator,
|
|
134
|
+
sign,
|
|
135
|
+
value,
|
|
136
|
+
values
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
if (value.filter || value.table || value.placeholder) return null;
|
|
140
|
+
const field = value.field;
|
|
141
|
+
if (!field) return null;
|
|
142
|
+
/** @type {boolean?} */
|
|
143
|
+
let defResult = false;
|
|
144
|
+
for (const item of records) {
|
|
145
|
+
if (!item || typeof item !== "object") continue;
|
|
146
|
+
if (!Object.hasOwn(item, field)) continue;
|
|
147
|
+
if (!Object.hasOwn(item, lastField)) continue;
|
|
148
|
+
defResult = null;
|
|
149
|
+
const result = yield {
|
|
150
|
+
fields: currentFields,
|
|
151
|
+
operator,
|
|
152
|
+
sign,
|
|
153
|
+
value: item[field],
|
|
154
|
+
values: [item[lastField]]
|
|
155
|
+
};
|
|
156
|
+
if (result === null) continue;
|
|
157
|
+
return result;
|
|
158
|
+
}
|
|
159
|
+
return defResult;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
*
|
|
163
|
+
* @param {Record<string, any>[]} data
|
|
164
|
+
* @param {string[]} parentFields
|
|
165
|
+
* @param {Match | Children | Or} match
|
|
166
|
+
* @returns {Generator<ExecParam, boolean?, boolean?>}
|
|
167
|
+
*/
|
|
168
|
+
function* execItem(data, parentFields, match) {
|
|
169
|
+
if (!Array.isArray(match)) {
|
|
170
|
+
const { and, or } = match;
|
|
171
|
+
return yield* execOr(data, parentFields, and, or);
|
|
172
|
+
}
|
|
173
|
+
if (match.length !== 2) return yield* execMatch(data, parentFields, match);
|
|
174
|
+
/** @type {boolean?} */
|
|
175
|
+
let defResult = null;
|
|
176
|
+
const values = select(data, match[0]);
|
|
177
|
+
if (!values.length) return false;
|
|
178
|
+
const fields = [...parentFields, ...match[0].map((v) => typeof v === "string" ? v : v[0])];
|
|
179
|
+
for (const item of match[1]) {
|
|
180
|
+
const result = yield* execItem(values, fields, item);
|
|
181
|
+
if (result === null) continue;
|
|
182
|
+
if (!result) return false;
|
|
183
|
+
defResult = true;
|
|
184
|
+
}
|
|
185
|
+
return defResult;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
*
|
|
189
|
+
* @param {Record<string, any>[]} list
|
|
190
|
+
* @param {string[]} parentFields
|
|
191
|
+
* @param {(Match | Children)[]} [and]
|
|
192
|
+
* @param {(Match | Children | Or)[]} [or]
|
|
193
|
+
* @returns {Generator<ExecParam, boolean?, boolean?>}
|
|
194
|
+
*/
|
|
195
|
+
function* execOr(list, parentFields, and, or) {
|
|
196
|
+
/** @type {boolean?} */
|
|
197
|
+
let defResult = null;
|
|
198
|
+
if (Array.isArray(and)) for (const item of and) {
|
|
199
|
+
const result = yield* execItem(list, parentFields, item);
|
|
200
|
+
if (result === null) continue;
|
|
201
|
+
if (!result) return false;
|
|
202
|
+
defResult = true;
|
|
203
|
+
}
|
|
204
|
+
if (Array.isArray(or)) for (const item of or) {
|
|
205
|
+
const result = yield* execItem(list, parentFields, item);
|
|
206
|
+
if (result === null) continue;
|
|
207
|
+
if (result) return true;
|
|
208
|
+
defResult = true;
|
|
209
|
+
}
|
|
210
|
+
return defResult;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
*
|
|
214
|
+
* @param {Record<string, any>} data
|
|
215
|
+
* @param {(value: ExecParam) => boolean?} exec
|
|
216
|
+
* @param {(Match | Children)[]} [and]
|
|
217
|
+
* @param {(Match | Children | Or)[]} [or]
|
|
218
|
+
*/
|
|
219
|
+
function exec(data, exec, and, or) {
|
|
220
|
+
return runGenerator(execOr([data].flat(), [], and, or), exec, null);
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
*
|
|
224
|
+
* @param {Record<string, any>} data
|
|
225
|
+
* @param {(value: ExecParam) => PromiseLike<boolean?> | boolean?} exec
|
|
226
|
+
* @param {(Match | Children)[]} [and]
|
|
227
|
+
* @param {(Match | Children | Or)[]} [or]
|
|
228
|
+
*/
|
|
229
|
+
async function asyncExec(data, exec, and, or) {
|
|
230
|
+
return asyncRunGenerator(execOr([data].flat(), [], and, or), exec, null);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
//#endregion
|
|
234
|
+
//#region packages/common/Search/fill.mjs
|
|
235
|
+
/** @import { Value } from './types.mjs' */
|
|
236
|
+
/** @import { Children, Field, FieldValue, Match, Or } from './types.mjs' */
|
|
237
|
+
/**
|
|
238
|
+
*
|
|
239
|
+
* @template {Match | Children | Or} T
|
|
240
|
+
* @param {T[]} match
|
|
241
|
+
* @returns {Generator<Value | (() => Value), T[]?, Value | (() => string | null) | null>}
|
|
242
|
+
*/
|
|
243
|
+
function* fillList(match) {
|
|
244
|
+
/** @type {T[]} */
|
|
245
|
+
let list = [];
|
|
246
|
+
let has = false;
|
|
247
|
+
for (const child of match) {
|
|
248
|
+
const val = yield* fillMatch(child);
|
|
249
|
+
list.push(val || child);
|
|
250
|
+
if (val) has = true;
|
|
251
|
+
}
|
|
252
|
+
return has ? list : null;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
*
|
|
256
|
+
* @template {Match | Children | Or} T
|
|
257
|
+
* @param {T} match
|
|
258
|
+
* @returns {Generator<Value | (() => Value), T?, Value | (() => string | null) | null>}
|
|
259
|
+
*/
|
|
260
|
+
function* fillMatch(match) {
|
|
261
|
+
if (!Array.isArray(match)) {
|
|
262
|
+
const and = match.and && (yield* fillMatches(match.and));
|
|
263
|
+
const or = match.or && (yield* fillMatches(match.or));
|
|
264
|
+
if (!and && !or) return null;
|
|
265
|
+
return {
|
|
266
|
+
and: and || match.and,
|
|
267
|
+
or: or || match.or
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
if (match.length === 2) {
|
|
271
|
+
if (!(yield* fillMatches(match[1]))) return null;
|
|
272
|
+
return [match[0], match[1]];
|
|
273
|
+
}
|
|
274
|
+
const value = yield match[1];
|
|
275
|
+
if (value === null) return null;
|
|
276
|
+
return [
|
|
277
|
+
match[0],
|
|
278
|
+
value,
|
|
279
|
+
match[2],
|
|
280
|
+
match[3]
|
|
281
|
+
];
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
*
|
|
285
|
+
* @template {Match | Children | Or} T
|
|
286
|
+
* @param {T[]} match
|
|
287
|
+
* @returns {Generator<Value | (() => Value), T[]?, Value | (() => string | null) | null>}
|
|
288
|
+
*/
|
|
289
|
+
function* fillMatches(match) {
|
|
290
|
+
/** @type {T[]} */
|
|
291
|
+
const list = [];
|
|
292
|
+
let has = false;
|
|
293
|
+
for (const child of match) {
|
|
294
|
+
const val = yield* fillMatch(child);
|
|
295
|
+
list.push(val || child);
|
|
296
|
+
if (val) has = true;
|
|
297
|
+
}
|
|
298
|
+
return has ? list : null;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
*
|
|
302
|
+
* @template {Match | Children | Or} T
|
|
303
|
+
* @param {T[] | null | undefined} match
|
|
304
|
+
* @param {(placeholder: string) => string | (() => string | null) | null} getPlaceholder
|
|
305
|
+
* @returns {T[]}
|
|
306
|
+
*/
|
|
307
|
+
function fill(match, getPlaceholder) {
|
|
308
|
+
if (!match) return [];
|
|
309
|
+
/**
|
|
310
|
+
*
|
|
311
|
+
* @template {Value | (() => Value)} T
|
|
312
|
+
* @param {T} match
|
|
313
|
+
* @returns {Value | (() => string | null) | null}
|
|
314
|
+
*/
|
|
315
|
+
function fillValue(match) {
|
|
316
|
+
if (!match || typeof match !== "object") return null;
|
|
317
|
+
if (Array.isArray(match)) return null;
|
|
318
|
+
const placeholder = match.placeholder;
|
|
319
|
+
if (!placeholder || typeof placeholder !== "string") return null;
|
|
320
|
+
return getPlaceholder(placeholder);
|
|
321
|
+
}
|
|
322
|
+
return runGenerator(fillList(match), fillValue, null) || match;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
//#endregion
|
|
326
|
+
//#region packages/common/Search/parse.mjs
|
|
327
|
+
/** @import { RecursiveArray } from '../types/types.mjs' */
|
|
328
|
+
/** @import { Children, Field, Match, Or, Param, Value } from './types.mjs' */
|
|
329
|
+
/**
|
|
330
|
+
* @typedef {object} Parse.Field
|
|
331
|
+
* @property {string} field
|
|
332
|
+
* @property {string?} [group]
|
|
333
|
+
* @property {number[]} sub
|
|
334
|
+
*/
|
|
335
|
+
/**
|
|
336
|
+
* @typedef {object} Parse.Match
|
|
337
|
+
* @property {null} [children]
|
|
338
|
+
* @property {Parse.Field[]} fields
|
|
339
|
+
* @property {string?} operator
|
|
340
|
+
* @property {string} [sign]
|
|
341
|
+
* @property {Value} value
|
|
342
|
+
*/
|
|
343
|
+
/**
|
|
344
|
+
* @typedef {object} Parse.Children
|
|
345
|
+
* @property {Parse.Field[]} fields
|
|
346
|
+
* @property {(Parse.Match | Parse.Children)[]} children
|
|
347
|
+
*/
|
|
348
|
+
/**
|
|
349
|
+
*
|
|
350
|
+
* @param {Parse.Field[]} fields
|
|
351
|
+
* @returns {Field[]}
|
|
352
|
+
*/
|
|
353
|
+
function compressFields(fields) {
|
|
354
|
+
return fields.map(({ field, sub }) => sub.length ? [field, ...sub] : field);
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
*
|
|
358
|
+
* @param {Parse.Match | Parse.Children} v
|
|
359
|
+
* @returns {Match | Children}
|
|
360
|
+
*/
|
|
361
|
+
function compressAndItem(v) {
|
|
362
|
+
if (v.children) return [compressFields(v.fields), v.children.map(compressAndItem)];
|
|
363
|
+
const { fields, operator, value, sign } = v;
|
|
364
|
+
return [
|
|
365
|
+
compressFields(fields),
|
|
366
|
+
value,
|
|
367
|
+
operator,
|
|
368
|
+
sign
|
|
369
|
+
];
|
|
370
|
+
}
|
|
371
|
+
const fieldParserRegex = /^([-_\d\p{L}]+)((?:\[-?\d+\])*)$/u;
|
|
372
|
+
/**
|
|
373
|
+
*
|
|
374
|
+
* @param {string} l
|
|
375
|
+
*/
|
|
376
|
+
function parseFields(l) {
|
|
377
|
+
/** @type {Parse.Field[]} */
|
|
378
|
+
const fields = [];
|
|
379
|
+
for (const v of l.split(".")) {
|
|
380
|
+
if (!v || v[0] === "?") return null;
|
|
381
|
+
const index = v.indexOf("?");
|
|
382
|
+
const r = fieldParserRegex.exec(index > 0 ? v.slice(0, index) : v);
|
|
383
|
+
if (!r) return null;
|
|
384
|
+
const sub = r[2]?.split(/\[|\]/).filter(Boolean).map((v) => parseInt(v) || 0) || [];
|
|
385
|
+
const group = index > 0 ? v.slice(index) : void 0;
|
|
386
|
+
fields.push({
|
|
387
|
+
field: r[1],
|
|
388
|
+
group,
|
|
389
|
+
sub
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
return fields;
|
|
393
|
+
}
|
|
394
|
+
/** @param {string} v */
|
|
395
|
+
const decodeValue = (v) => decodeURIComponent(v.replaceAll("+", " "));
|
|
396
|
+
/**
|
|
397
|
+
*
|
|
398
|
+
* @param {string} s
|
|
399
|
+
* @returns {string | RecursiveArray<string>?}
|
|
400
|
+
*/
|
|
401
|
+
function parseArrayValue(s) {
|
|
402
|
+
/** @type {(string | RecursiveArray<string>)[]} */
|
|
403
|
+
const root = [];
|
|
404
|
+
/** @type {(string | RecursiveArray<string>)[][]} */
|
|
405
|
+
const stack = [];
|
|
406
|
+
let current = root;
|
|
407
|
+
/** @type {string[]} */
|
|
408
|
+
let strings = [];
|
|
409
|
+
let end = false;
|
|
410
|
+
let must = true;
|
|
411
|
+
function add() {
|
|
412
|
+
current.push(decodeValue(strings.join()));
|
|
413
|
+
strings = [];
|
|
414
|
+
}
|
|
415
|
+
for (const c of s) {
|
|
416
|
+
if (c === ",") {
|
|
417
|
+
if (end) end = false;
|
|
418
|
+
else add();
|
|
419
|
+
must = true;
|
|
420
|
+
continue;
|
|
421
|
+
}
|
|
422
|
+
if (c === ")") {
|
|
423
|
+
if (must || strings.length) {
|
|
424
|
+
add();
|
|
425
|
+
must = false;
|
|
426
|
+
}
|
|
427
|
+
const last = stack.pop();
|
|
428
|
+
if (!last) return null;
|
|
429
|
+
last.push(current);
|
|
430
|
+
current = last;
|
|
431
|
+
end = true;
|
|
432
|
+
continue;
|
|
433
|
+
}
|
|
434
|
+
if (end) return null;
|
|
435
|
+
must = false;
|
|
436
|
+
if (c !== "(") {
|
|
437
|
+
strings.push(c);
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
if (strings.length) return null;
|
|
441
|
+
stack.push(current);
|
|
442
|
+
current = [];
|
|
443
|
+
}
|
|
444
|
+
if (must || strings.length) add();
|
|
445
|
+
if (stack.length) return null;
|
|
446
|
+
return root.length === 1 ? root[0] : root;
|
|
447
|
+
}
|
|
448
|
+
const valueQRegex = /^([-_\d\p{L}]+)@([-_\d\p{L}]+)/u;
|
|
449
|
+
/**
|
|
450
|
+
*
|
|
451
|
+
* @param {string?} v
|
|
452
|
+
* @returns {Value | true}
|
|
453
|
+
*/
|
|
454
|
+
function parseValue(v) {
|
|
455
|
+
if (v === null) return true;
|
|
456
|
+
if (v[0] === "?") return { placeholder: decodeValue(v.slice(1)) };
|
|
457
|
+
if (v[0] === ":") return { field: decodeValue(v.slice(1)) };
|
|
458
|
+
if (v[0] === "@") return null;
|
|
459
|
+
if (valueQRegex.test(v)) return null;
|
|
460
|
+
if (v.includes("(") || v.includes(")")) {
|
|
461
|
+
const list = parseArrayValue(v);
|
|
462
|
+
if (list) return list;
|
|
463
|
+
}
|
|
464
|
+
if (v.includes(",")) return v.split(",").map((v) => decodeValue(v.replaceAll("+", " ")));
|
|
465
|
+
return decodeValue(v);
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
*
|
|
469
|
+
* @param {(Parse.Match | Parse.Children)[]} list
|
|
470
|
+
* @returns {Iterable<Match | Children>}
|
|
471
|
+
*/
|
|
472
|
+
function* toAnd(list) {
|
|
473
|
+
/** @type {[(Parse.Match | Parse.Children), Parse.Field[], Parse.Field[]][]} */
|
|
474
|
+
const values = list.map((v) => {
|
|
475
|
+
const fields = v.fields;
|
|
476
|
+
const index = fields.findLastIndex((v) => v.group);
|
|
477
|
+
if (index < 0 || index >= fields.length - 1) return [
|
|
478
|
+
v,
|
|
479
|
+
[],
|
|
480
|
+
[]
|
|
481
|
+
];
|
|
482
|
+
const field = fields[index];
|
|
483
|
+
if (!field) return [
|
|
484
|
+
v,
|
|
485
|
+
[],
|
|
486
|
+
[]
|
|
487
|
+
];
|
|
488
|
+
return [
|
|
489
|
+
{
|
|
490
|
+
...v,
|
|
491
|
+
fields: fields.slice(index + 1)
|
|
492
|
+
},
|
|
493
|
+
fields.slice(0, index + 1),
|
|
494
|
+
[...fields.slice(0, index), {
|
|
495
|
+
...field,
|
|
496
|
+
group: void 0
|
|
497
|
+
}]
|
|
498
|
+
];
|
|
499
|
+
});
|
|
500
|
+
const map = Object.groupBy(values, (v) => JSON.stringify(v[1]));
|
|
501
|
+
yield* map["[]"]?.map((v) => compressAndItem(v[0])) || [];
|
|
502
|
+
delete map["[]"];
|
|
503
|
+
/** @type {Parse.Children[]} */
|
|
504
|
+
const result = Object.values(map).filter(Boolean).map((list = []) => {
|
|
505
|
+
if (!list?.length) return {
|
|
506
|
+
fields: [],
|
|
507
|
+
children: []
|
|
508
|
+
};
|
|
509
|
+
return {
|
|
510
|
+
fields: list[0][2] || [],
|
|
511
|
+
children: list.map((v) => v[0])
|
|
512
|
+
};
|
|
513
|
+
}).filter((v) => v.fields.length);
|
|
514
|
+
if (!result.length) return;
|
|
515
|
+
yield* toAnd(result);
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
*
|
|
519
|
+
* @param {(Parse.Match & {groups: string[]})[]} list
|
|
520
|
+
* @returns {Or}
|
|
521
|
+
*/
|
|
522
|
+
function toOr(list) {
|
|
523
|
+
const map = Map.groupBy(list, (v) => v.groups.shift() ?? null);
|
|
524
|
+
const and = map.get(null) || [];
|
|
525
|
+
const baseOr = map.get("") || [];
|
|
526
|
+
map.delete(null);
|
|
527
|
+
map.delete("");
|
|
528
|
+
return {
|
|
529
|
+
or: [...baseOr.map((v) => compressAndItem(v)), ...[...map.values()].map((v) => toOr(v))],
|
|
530
|
+
and: [...toAnd(and)]
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
*
|
|
535
|
+
* @param {string} search
|
|
536
|
+
* @returns {Param}
|
|
537
|
+
*/
|
|
538
|
+
function parse(search, split = "&") {
|
|
539
|
+
/** @type {string[]} */
|
|
540
|
+
const values = [];
|
|
541
|
+
/** @type {string[]} */
|
|
542
|
+
const select = [];
|
|
543
|
+
/** @type {[field: string, desc?: boolean][]} */
|
|
544
|
+
const sorts = [];
|
|
545
|
+
let limit = 0;
|
|
546
|
+
let offset = 0;
|
|
547
|
+
let page = 0;
|
|
548
|
+
/** @type {Record<string, string>} */
|
|
549
|
+
const placeholders = {};
|
|
550
|
+
/** @type {(Parse.Match & {groups: string[]})[]} */
|
|
551
|
+
const allMatches = [];
|
|
552
|
+
for (const s of search.split(split)) {
|
|
553
|
+
if (s[0] === "=") {
|
|
554
|
+
values.push(s.slice(1));
|
|
555
|
+
continue;
|
|
556
|
+
}
|
|
557
|
+
const index = s.indexOf("=");
|
|
558
|
+
const key = index < 0 ? s : s.slice(0, index);
|
|
559
|
+
const val = index < 0 ? null : s.slice(index + 1);
|
|
560
|
+
if (index <= 0) continue;
|
|
561
|
+
if (`.!@^*<>{}[]()+|;,"'\`\\/`.includes(key[0])) continue;
|
|
562
|
+
if (key[0] === "?") {
|
|
563
|
+
placeholders[decodeValue(key.slice(1))] = decodeValue(val || "");
|
|
564
|
+
continue;
|
|
565
|
+
}
|
|
566
|
+
if (key[0] === ":") {
|
|
567
|
+
const name = key.slice(1);
|
|
568
|
+
if (!val) continue;
|
|
569
|
+
if (name === "sort") sorts.push(...val.split(",").filter(Boolean).map((v) => {
|
|
570
|
+
const desc = v[0] === "-";
|
|
571
|
+
return [decodeValue(desc ? v.slice(1) : v), desc];
|
|
572
|
+
}).filter(Boolean));
|
|
573
|
+
else if (name === "select") select.push(...val.split(",").map(decodeValue).filter(Boolean));
|
|
574
|
+
else if (name === "limit") {
|
|
575
|
+
if (val && /^\d+$/.test(val)) limit = parseInt(val);
|
|
576
|
+
} else if (name === "offset") {
|
|
577
|
+
if (val && /^\d+$/.test(val)) offset = parseInt(val);
|
|
578
|
+
} else if (name === "page") {
|
|
579
|
+
if (val && /^\d+$/.test(val)) page = parseInt(val);
|
|
580
|
+
}
|
|
581
|
+
continue;
|
|
582
|
+
}
|
|
583
|
+
const sg = key.split("~");
|
|
584
|
+
let field = sg.pop() || "";
|
|
585
|
+
let sign = "";
|
|
586
|
+
if ("!".includes(field[field.length - 1])) {
|
|
587
|
+
sign = "!";
|
|
588
|
+
field = field.slice(0, -1);
|
|
589
|
+
}
|
|
590
|
+
const l = field.split(":", 3);
|
|
591
|
+
if (l.length > 2) continue;
|
|
592
|
+
const fields = parseFields(l[0]);
|
|
593
|
+
if (!fields) continue;
|
|
594
|
+
const value = parseValue(val);
|
|
595
|
+
if (value === null) continue;
|
|
596
|
+
const operator = decodeValue(l[1] || "");
|
|
597
|
+
const groupIndex = sg.indexOf("");
|
|
598
|
+
const groups = groupIndex < 0 ? sg : sg.slice(0, groupIndex + 1);
|
|
599
|
+
allMatches.push({
|
|
600
|
+
groups,
|
|
601
|
+
fields,
|
|
602
|
+
operator: !operator && value === true ? null : operator,
|
|
603
|
+
sign,
|
|
604
|
+
value: value === true ? null : value
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
const { and, or } = toOr(allMatches);
|
|
608
|
+
/** @type {Param} */
|
|
609
|
+
const result = {};
|
|
610
|
+
if (values?.length) result.values = values;
|
|
611
|
+
if (limit && limit > 0) result.limit = limit;
|
|
612
|
+
if (page && page > 0) result.page = page;
|
|
613
|
+
if (offset && offset > 0) result.offset = offset;
|
|
614
|
+
if (select?.length) result.select = select;
|
|
615
|
+
if (sorts?.length) result.sort = sorts;
|
|
616
|
+
const where = and;
|
|
617
|
+
if (where?.length) result.where = where;
|
|
618
|
+
const orWhere = or;
|
|
619
|
+
if (orWhere?.length) result.orWhere = orWhere;
|
|
620
|
+
if (Array.isArray(placeholders) && placeholders.length) result.placeholders = placeholders;
|
|
621
|
+
return result;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
//#endregion
|
|
625
|
+
//#region packages/common/Search/merge.mjs
|
|
626
|
+
/** @import { Param } from './types.mjs' */
|
|
627
|
+
/**
|
|
628
|
+
*
|
|
629
|
+
* @param {Partial<Param>} a
|
|
630
|
+
* @param {Partial<Param>} b
|
|
631
|
+
* @returns {Param}
|
|
632
|
+
*/
|
|
633
|
+
function merge(a, b) {
|
|
634
|
+
const limit = b.limit || a.limit || 10;
|
|
635
|
+
const page = b.page ?? a.page;
|
|
636
|
+
const offset = b.offset ?? (b.limit && (b.page || b.page === 0) ? (Math.floor(Math.max(b.page || 0, 1)) - 1) * b.limit : null) ?? a.offset ?? (a.limit && (a.page || a.page === 0) ? (Math.floor(Math.max(a.page || 0, 1)) - 1) * a.limit : null) ?? (Math.floor(Math.max(page || 0, 1)) - 1) * limit;
|
|
637
|
+
const values = [...a.values || [], ...b.values || []];
|
|
638
|
+
const sort = [...a.sort || [], ...b.sort || []];
|
|
639
|
+
const select = [...a.select || [], ...b.select || []];
|
|
640
|
+
const where = [...a.where || [], ...b.where || []];
|
|
641
|
+
const orWhere = [...a.orWhere || [], ...b.orWhere || []];
|
|
642
|
+
const or = [...a.or || [], ...b.or || []];
|
|
643
|
+
/** @type {Param} */
|
|
644
|
+
const result = {};
|
|
645
|
+
if (values?.length) result.values = values;
|
|
646
|
+
if (limit && limit > 0) result.limit = limit;
|
|
647
|
+
if (page && page > 0) result.page = page;
|
|
648
|
+
if (offset && offset > 0) result.offset = offset;
|
|
649
|
+
if (select?.length) result.select = select;
|
|
650
|
+
if (sort?.length) result.sort = sort;
|
|
651
|
+
if (where?.length) result.where = where;
|
|
652
|
+
if (orWhere?.length) result.orWhere = orWhere;
|
|
653
|
+
if (or?.length) result.or = or;
|
|
654
|
+
return result;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
//#endregion
|
|
658
|
+
//#region packages/common/Search/stringify.mjs
|
|
659
|
+
/** @import { RecursiveArray } from '../types/types.mjs' */
|
|
660
|
+
/** @import { Children, Field, FieldValue, Match, Or, Param } from './types.mjs' */
|
|
661
|
+
/** @param {string} v */
|
|
662
|
+
const encodeValue = (v) => encodeURIComponent(v).replaceAll("%20", "+");
|
|
663
|
+
/**
|
|
664
|
+
*
|
|
665
|
+
* @param {string | RecursiveArray<string> | FieldValue} value
|
|
666
|
+
*/
|
|
667
|
+
function stringifyValue(value) {
|
|
668
|
+
if (Array.isArray(value)) {
|
|
669
|
+
/**
|
|
670
|
+
*
|
|
671
|
+
* @param {string | RecursiveArray<string>} v
|
|
672
|
+
* @returns {string}
|
|
673
|
+
*/
|
|
674
|
+
function s(v) {
|
|
675
|
+
if (Array.isArray(v)) return `(${v.map(s).join(",")})`;
|
|
676
|
+
return encodeValue(v);
|
|
677
|
+
}
|
|
678
|
+
const val = value.map(s).join(",");
|
|
679
|
+
if (value.length > 1) return val;
|
|
680
|
+
return `(${val})`;
|
|
681
|
+
}
|
|
682
|
+
if (typeof value !== "object") return encodeValue(value);
|
|
683
|
+
const { field, placeholder } = value;
|
|
684
|
+
if (placeholder) return `?${encodeValue(placeholder)}`;
|
|
685
|
+
if (field) return `:${encodeValue(field)}`;
|
|
686
|
+
return "";
|
|
687
|
+
}
|
|
688
|
+
/**
|
|
689
|
+
*
|
|
690
|
+
* @param {Field[]} fields
|
|
691
|
+
*/
|
|
692
|
+
function stringifyFields(fields) {
|
|
693
|
+
return fields.map((item) => {
|
|
694
|
+
const [field, ...sub] = typeof item === "string" ? [item] : item;
|
|
695
|
+
return `${encodeValue(field)}${sub.map(() => `[k]`).join("")}`;
|
|
696
|
+
}).join(".");
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
*
|
|
700
|
+
* @param {Match} define
|
|
701
|
+
*/
|
|
702
|
+
function stringifyDefine([fields, value, operator, sign]) {
|
|
703
|
+
const key = operator ? `${stringifyFields(fields)}:${encodeValue(operator)}${sign || ""}` : `${stringifyFields(fields)}${sign || ""}`;
|
|
704
|
+
if (operator === null) return key;
|
|
705
|
+
if (value === null) return "";
|
|
706
|
+
if (typeof value === "function") return "";
|
|
707
|
+
return `${key}=${stringifyValue(value)}`;
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
*
|
|
711
|
+
* @param {(Match | Children)[]} list
|
|
712
|
+
* @returns {string[]}
|
|
713
|
+
*/
|
|
714
|
+
function stringifyAnd(list) {
|
|
715
|
+
let index = 0;
|
|
716
|
+
return list.flatMap((v) => {
|
|
717
|
+
if (v.length !== 2) return stringifyDefine(v);
|
|
718
|
+
const list = stringifyAnd(v[1]);
|
|
719
|
+
if (!list.length) return [];
|
|
720
|
+
let key = stringifyFields(v[0]);
|
|
721
|
+
if (list.length > 1) key += `?${(index++).toString(36)}`;
|
|
722
|
+
return list.map((v) => `${key}.${v}`);
|
|
723
|
+
}).filter(Boolean);
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
*
|
|
727
|
+
* @param {(Match | Children | Or)[]} list
|
|
728
|
+
* @returns {string[]}
|
|
729
|
+
*/
|
|
730
|
+
function stringifyOr(list) {
|
|
731
|
+
let index = 0;
|
|
732
|
+
return list.map((v) => {
|
|
733
|
+
if (Array.isArray(v)) return stringifyAnd([v]);
|
|
734
|
+
return [...stringifyAnd(v.and || []), ...stringifyOr(v.or || [])];
|
|
735
|
+
}).flatMap((list) => {
|
|
736
|
+
if (!list) return [];
|
|
737
|
+
const key = list.length === 1 ? `~` : `${(index++).toString(36)}~`;
|
|
738
|
+
return list.map((v) => `${key}${v}`);
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
*
|
|
743
|
+
* @param {Partial<Param>} query
|
|
744
|
+
* @param {boolean} [usePage]
|
|
745
|
+
*/
|
|
746
|
+
function stringify({ values, where, orWhere, sort, select, limit, offset, page, placeholders }, usePage) {
|
|
747
|
+
/** @type {string[]} */
|
|
748
|
+
let queries = [];
|
|
749
|
+
for (const v of values || []) {
|
|
750
|
+
if (!v) continue;
|
|
751
|
+
queries.push(`=${encodeValue(v)}`);
|
|
752
|
+
}
|
|
753
|
+
if (limit && limit > 0) queries.push(`:limit=${limit}`);
|
|
754
|
+
if (usePage) {
|
|
755
|
+
if (page && page > 0) queries.push(`:page=${page}`);
|
|
756
|
+
} else if (offset && offset > 0) queries.push(`:offset=${offset}`);
|
|
757
|
+
if (select) {
|
|
758
|
+
const val = select.filter(Boolean).map(encodeValue).join(",");
|
|
759
|
+
if (val) queries.push(`.select=${val}`);
|
|
760
|
+
}
|
|
761
|
+
if (sort) {
|
|
762
|
+
const val = sort.filter(Boolean).map((v) => {
|
|
763
|
+
if (Array.isArray(v)) {
|
|
764
|
+
const [field, desc] = v;
|
|
765
|
+
if (field) return `${desc ? "-" : ""}${encodeValue(field)}`;
|
|
766
|
+
} else if (v) return encodeValue(v);
|
|
767
|
+
return "";
|
|
768
|
+
}).filter(Boolean).join(",");
|
|
769
|
+
if (val) queries.push(`.sort=${val}`);
|
|
770
|
+
}
|
|
771
|
+
if (placeholders && typeof placeholders === "object" && !Array.isArray(placeholders)) for (const [k, v] of Object.entries(placeholders)) queries.push(`?${encodeValue(k)}=${encodeValue(v)}`);
|
|
772
|
+
for (const v of stringifyAnd(where || [])) queries.push(v);
|
|
773
|
+
for (const v of stringifyOr(orWhere || [])) queries.push(v);
|
|
774
|
+
return queries.join("&");
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
//#endregion
|
|
778
|
+
//#region packages/common/Search/index.mjs
|
|
779
|
+
var Search_exports = /* @__PURE__ */ __exportAll({
|
|
780
|
+
asyncExec: () => asyncExec,
|
|
781
|
+
asyncRunGenerator: () => asyncRunGenerator,
|
|
782
|
+
exec: () => exec,
|
|
783
|
+
fill: () => fill,
|
|
784
|
+
fillMatches: () => fillMatches,
|
|
785
|
+
merge: () => merge,
|
|
786
|
+
parse: () => parse,
|
|
787
|
+
runGenerator: () => runGenerator,
|
|
788
|
+
stringify: () => stringify
|
|
789
|
+
});
|
|
790
|
+
|
|
791
|
+
//#endregion
|
|
792
|
+
//#region packages/common/Hook.mjs
|
|
793
|
+
/**
|
|
794
|
+
* @template T
|
|
795
|
+
* @typedef {T | readonly T[]} Hook.MaybeArray
|
|
796
|
+
*/
|
|
797
|
+
/**
|
|
798
|
+
* @template T
|
|
799
|
+
* @typedef {T extends readonly any[] ? Readonly<T>
|
|
800
|
+
* : T extends Record<string, any> ? Hook.MaybeArray<Hook.Define<T> | T>
|
|
801
|
+
* : T extends object ? Hook.MaybeArray<Hook.Define<T> | T>
|
|
802
|
+
* : Hook.MaybeArray<T>
|
|
803
|
+
* } Hook.Item
|
|
804
|
+
*/
|
|
805
|
+
/**
|
|
806
|
+
* @template T
|
|
807
|
+
* @typedef {T extends Record<string, any> ? { [k in keyof T]?: Hook.Item<T[k]> } : T} Hook.Define
|
|
808
|
+
*/
|
|
809
|
+
/** @type {*} */
|
|
810
|
+
const noopFilter = () => true;
|
|
811
|
+
/**
|
|
812
|
+
*
|
|
813
|
+
* @param {Record<string, any>} plugin
|
|
814
|
+
* @param {string[]} name
|
|
815
|
+
* @returns {*[]}
|
|
816
|
+
*/
|
|
817
|
+
function findHook(plugin, name) {
|
|
818
|
+
let list = [plugin].flat();
|
|
819
|
+
for (const k of name.filter(Boolean)) {
|
|
820
|
+
const values = [];
|
|
821
|
+
for (const it of list) {
|
|
822
|
+
if (!it || typeof it !== "object") continue;
|
|
823
|
+
if (!Object.hasOwn(it, k)) continue;
|
|
824
|
+
values.push(it[k]);
|
|
825
|
+
}
|
|
826
|
+
if (!values.length) return [];
|
|
827
|
+
list = values.flat();
|
|
828
|
+
}
|
|
829
|
+
return list;
|
|
830
|
+
}
|
|
831
|
+
/**
|
|
832
|
+
*
|
|
833
|
+
* @template T
|
|
834
|
+
* @param {any} plugin
|
|
835
|
+
* @param {string[]} names
|
|
836
|
+
* @returns {T | void}
|
|
837
|
+
*/
|
|
838
|
+
function first(plugin, names) {
|
|
839
|
+
if (!names.length) return plugin;
|
|
840
|
+
if (!plugin || typeof plugin !== "object") return;
|
|
841
|
+
const [name, ...keys] = names;
|
|
842
|
+
for (const v of [plugin].flat()) {
|
|
843
|
+
const val = first(v[name], keys);
|
|
844
|
+
if (val !== void 0) return val;
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
/**
|
|
848
|
+
*
|
|
849
|
+
* @template T
|
|
850
|
+
* @param {Record<string, any>} hooks
|
|
851
|
+
* @param {string[]} name
|
|
852
|
+
* @returns {IterableIterator<T>}
|
|
853
|
+
*/
|
|
854
|
+
function* values(hooks, name) {
|
|
855
|
+
for (const it of Object.values(hooks)) for (const value of findHook(it, name)) yield value;
|
|
856
|
+
}
|
|
857
|
+
/**
|
|
858
|
+
* @template T
|
|
859
|
+
*/
|
|
860
|
+
var Hook = class Hook {
|
|
861
|
+
/**
|
|
862
|
+
* @template {object} T
|
|
863
|
+
* @param {Record<string, Hook.Define<T>> | (() => Record<string, Hook.Define<T>>)} [options]
|
|
864
|
+
* @return {Hook<T>}
|
|
865
|
+
*/
|
|
866
|
+
static build(options) {
|
|
867
|
+
const hook = new Hook();
|
|
868
|
+
return Hook.update(hook, options);
|
|
869
|
+
}
|
|
870
|
+
/**
|
|
871
|
+
*
|
|
872
|
+
* @template T
|
|
873
|
+
* @param {Hook<T>} hook
|
|
874
|
+
* @param {Record<string, Hook.Define<T>> | (() => Record<string, Hook.Define<T>>)} [options]
|
|
875
|
+
*/
|
|
876
|
+
static update(hook, options) {
|
|
877
|
+
let updated = false;
|
|
878
|
+
if (typeof options === "function") {
|
|
879
|
+
Object.defineProperty(hook.#config, "hooks", {
|
|
880
|
+
configurable: true,
|
|
881
|
+
get() {
|
|
882
|
+
const r = options();
|
|
883
|
+
return typeof r === "object" && r || {};
|
|
884
|
+
}
|
|
885
|
+
});
|
|
886
|
+
updated = true;
|
|
887
|
+
} else if (options && typeof options === "object") {
|
|
888
|
+
Object.defineProperty(hook.#config, "hooks", {
|
|
889
|
+
configurable: true,
|
|
890
|
+
value: options
|
|
891
|
+
});
|
|
892
|
+
updated = true;
|
|
893
|
+
}
|
|
894
|
+
if (updated) Hook.reset(hook);
|
|
895
|
+
return hook;
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
*
|
|
899
|
+
* @template T
|
|
900
|
+
* @param {Hook<T>} hook
|
|
901
|
+
*/
|
|
902
|
+
static reset(hook) {
|
|
903
|
+
const hooks = hook.#hooks;
|
|
904
|
+
const resetFn = hook.#config.resetFn;
|
|
905
|
+
resetFn.sort(([, a], [, b]) => a - b);
|
|
906
|
+
const before = resetFn.filter(([, p]) => p <= 0);
|
|
907
|
+
const after = resetFn.filter(([, p]) => p > 0);
|
|
908
|
+
for (const [fn] of before) fn();
|
|
909
|
+
for (const fn of values(hooks, ["reset"])) {
|
|
910
|
+
if (typeof fn !== "function") continue;
|
|
911
|
+
fn();
|
|
912
|
+
}
|
|
913
|
+
for (const [fn] of after) fn();
|
|
914
|
+
}
|
|
915
|
+
/** @type {{hooks: any; resetFn: [fn: () => void, hysteresis: number][]}} */
|
|
916
|
+
#config = {
|
|
917
|
+
hooks: Object.create(null),
|
|
918
|
+
resetFn: []
|
|
919
|
+
};
|
|
920
|
+
/** @type {string[]} */
|
|
921
|
+
#keys = [];
|
|
922
|
+
#stringTag = "Hook";
|
|
923
|
+
[Symbol.iterator]() {
|
|
924
|
+
return this.entries();
|
|
925
|
+
}
|
|
926
|
+
[Symbol.toStringTag]() {
|
|
927
|
+
return this.#stringTag;
|
|
928
|
+
}
|
|
929
|
+
/** @param {string} hint */
|
|
930
|
+
[Symbol.toPrimitive](hint) {
|
|
931
|
+
if (hint === "number") return NaN;
|
|
932
|
+
return `[${this[Symbol.toStringTag]()}]`;
|
|
933
|
+
}
|
|
934
|
+
get #hooks() {
|
|
935
|
+
return this.#config.hooks;
|
|
936
|
+
}
|
|
937
|
+
/**
|
|
938
|
+
* @template {T extends readonly any[] ? never : T extends object ? string & keyof T : never} K
|
|
939
|
+
* @param {K} key
|
|
940
|
+
* @returns {Hook<Exclude<T extends readonly any[] ? never : T extends object ? T[K] :never, void | undefined>>}
|
|
941
|
+
*/
|
|
942
|
+
get(key) {
|
|
943
|
+
const keys = [...this.#keys, key];
|
|
944
|
+
const hook = new Hook();
|
|
945
|
+
hook.#config = this.#config;
|
|
946
|
+
hook.#keys = keys;
|
|
947
|
+
hook.#stringTag = `Hook(${keys.join(".")})`;
|
|
948
|
+
return hook;
|
|
949
|
+
}
|
|
950
|
+
/**
|
|
951
|
+
* @param {T extends (...args: infer A) => any ? A : []} args
|
|
952
|
+
* @returns
|
|
953
|
+
*/
|
|
954
|
+
call(...args) {
|
|
955
|
+
for (const fn of this.values()) {
|
|
956
|
+
if (typeof fn !== "function") continue;
|
|
957
|
+
fn(...args);
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
/**
|
|
961
|
+
*
|
|
962
|
+
* @param {string} plugin
|
|
963
|
+
* @returns {T | void}
|
|
964
|
+
*/
|
|
965
|
+
first(plugin) {
|
|
966
|
+
const hooks = this.#hooks;
|
|
967
|
+
if (!Object.hasOwn(hooks, plugin)) return;
|
|
968
|
+
return first(hooks[plugin], this.#keys.filter(Boolean));
|
|
969
|
+
}
|
|
970
|
+
/**
|
|
971
|
+
*
|
|
972
|
+
* @param {string} plugin
|
|
973
|
+
* @returns {IterableIterator<T extends readonly (infer V)[] ? V : T>}
|
|
974
|
+
*/
|
|
975
|
+
*plugin(plugin) {
|
|
976
|
+
const hooks = this.#hooks;
|
|
977
|
+
if (!Object.hasOwn(hooks, plugin)) return;
|
|
978
|
+
yield* findHook(hooks[plugin], this.#keys.filter(Boolean));
|
|
979
|
+
}
|
|
980
|
+
/**
|
|
981
|
+
*
|
|
982
|
+
* @returns {IterableIterator<T extends readonly (infer V)[] ? V : T>}
|
|
983
|
+
*/
|
|
984
|
+
values() {
|
|
985
|
+
return values(this.#hooks, this.#keys);
|
|
986
|
+
}
|
|
987
|
+
/**
|
|
988
|
+
*
|
|
989
|
+
* @returns {IterableIterator<[string, T extends readonly (infer V)[] ? V : T]>}
|
|
990
|
+
*/
|
|
991
|
+
*entries() {
|
|
992
|
+
const hooks = this.#hooks;
|
|
993
|
+
const name = this.#keys;
|
|
994
|
+
for (const [plugin, it] of Object.entries(hooks)) for (const value of findHook(it, name)) yield [plugin, value];
|
|
995
|
+
}
|
|
996
|
+
/**
|
|
997
|
+
*
|
|
998
|
+
* @template [TX=(T extends Record<string, infer TT>? TT extends readonly (infer V)[] ? V : TT : never)]
|
|
999
|
+
* @param {(v: *)=>v is TX} filter
|
|
1000
|
+
* @returns {IterableIterator<[plugin: string, key: string, value: TX]>}
|
|
1001
|
+
*/
|
|
1002
|
+
*entriesObject(filter = noopFilter) {
|
|
1003
|
+
for (const [plugin, hooksMap] of this.entries()) {
|
|
1004
|
+
if (!hooksMap || typeof hooksMap !== "object") continue;
|
|
1005
|
+
for (const [k, v] of Object.entries(hooksMap)) {
|
|
1006
|
+
if (!filter(v)) continue;
|
|
1007
|
+
yield [
|
|
1008
|
+
plugin,
|
|
1009
|
+
k,
|
|
1010
|
+
v
|
|
1011
|
+
];
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
/**
|
|
1016
|
+
* @template [TT=(T extends Record<string, infer TT> ? TT : never)]
|
|
1017
|
+
* @param {(v: *)=>v is TT} [filter]
|
|
1018
|
+
* @returns {Record<string, TT>}
|
|
1019
|
+
*/
|
|
1020
|
+
named(filter = noopFilter) {
|
|
1021
|
+
/** @type {Record<string, TT>} */
|
|
1022
|
+
const result = Object.create(null);
|
|
1023
|
+
for (const hooksMap of this.values()) {
|
|
1024
|
+
if (!hooksMap || typeof hooksMap !== "object") continue;
|
|
1025
|
+
for (const [k, v] of Object.entries(hooksMap)) {
|
|
1026
|
+
if (!filter(v)) continue;
|
|
1027
|
+
result[k] = v;
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
return result;
|
|
1031
|
+
}
|
|
1032
|
+
/**
|
|
1033
|
+
*
|
|
1034
|
+
* @param {() => void} fn
|
|
1035
|
+
* @param {number} [hysteresis]
|
|
1036
|
+
* @returns
|
|
1037
|
+
*/
|
|
1038
|
+
listen(fn, hysteresis) {
|
|
1039
|
+
if (typeof fn !== "function") return () => {};
|
|
1040
|
+
const resetFn = this.#config.resetFn;
|
|
1041
|
+
/** @type {[() => void, number]} */
|
|
1042
|
+
const it = [fn, Number.isFinite(hysteresis) && hysteresis || 0];
|
|
1043
|
+
resetFn.push(it);
|
|
1044
|
+
resetFn.sort(([, a], [, b]) => b - a);
|
|
1045
|
+
return () => {
|
|
1046
|
+
const index = resetFn.findIndex((v) => v === it);
|
|
1047
|
+
if (index >= 0) resetFn.splice(index, 1);
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
};
|
|
1051
|
+
|
|
1052
|
+
//#endregion
|
|
1053
|
+
//#region packages/common/model.mjs
|
|
1054
|
+
const regex = /\{([^}{}]+)\}/g;
|
|
1055
|
+
/**
|
|
1056
|
+
*
|
|
1057
|
+
* @param {string?} [pattern]
|
|
1058
|
+
*/
|
|
1059
|
+
function getPatternFields(pattern) {
|
|
1060
|
+
if (!pattern || typeof pattern !== "string") return [];
|
|
1061
|
+
/** @type {string[]} */
|
|
1062
|
+
const fields = [];
|
|
1063
|
+
for (let result = regex.exec(pattern); result; result = regex.exec(pattern)) fields.push(result[1]);
|
|
1064
|
+
return fields;
|
|
1065
|
+
}
|
|
1066
|
+
/**
|
|
1067
|
+
*
|
|
1068
|
+
* @param {string?} [pattern]
|
|
1069
|
+
* @param {Record<string, any>?} [document]
|
|
1070
|
+
* @returns
|
|
1071
|
+
*/
|
|
1072
|
+
function runPattern(pattern, document) {
|
|
1073
|
+
if (!pattern || typeof pattern !== "string") return "";
|
|
1074
|
+
const doc = typeof document === "object" && document || {};
|
|
1075
|
+
return pattern.replace(regex, (_, v) => {
|
|
1076
|
+
if (Object.hasOwn(doc, v)) return doc[v] ?? "";
|
|
1077
|
+
return `{${v}}`;
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
//#endregion
|
|
1082
|
+
//#region packages/common/yieldPermissionConstraintFields.mjs
|
|
1083
|
+
/** @import { Permission } from '@yongdall/types' */
|
|
1084
|
+
/**
|
|
1085
|
+
*
|
|
1086
|
+
* @param {Permission} permission
|
|
1087
|
+
* @returns {Iterable<string>}
|
|
1088
|
+
*/
|
|
1089
|
+
function* yieldPermissionConstraintFields({ constraints, organizationField }) {
|
|
1090
|
+
if (organizationField) yield organizationField;
|
|
1091
|
+
if (!constraints) return;
|
|
1092
|
+
for (const [field, constraint] of Object.entries(constraints)) {
|
|
1093
|
+
if (!constraint) continue;
|
|
1094
|
+
const { value, values, user } = constraint;
|
|
1095
|
+
if (value !== void 0) yield field;
|
|
1096
|
+
else if (Array.isArray(values) && values.length) yield field;
|
|
1097
|
+
else if (values instanceof Set && values.size) yield field;
|
|
1098
|
+
else if (user) yield field;
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
//#endregion
|
|
1103
|
+
//#region packages/common/index.mjs
|
|
1104
|
+
const pluginIdRegex = /^(?:(?<scope>@[a-zA-Z\d_.-]+)\/)?(?<name>[a-zA-Z\d_.-]+)$/;
|
|
1105
|
+
const findPluginIdRegex = /^(?<pluginId>(?:(?<scope>@[a-zA-Z\d_.-]+)\/)?(?<name>[a-zA-Z\d_.-]+))(?:\/(?<path>[^#]*))?(?:#(?<hash>.*))?$/;
|
|
1106
|
+
/**
|
|
1107
|
+
* @typedef {object} Plugin
|
|
1108
|
+
* @property {string} [version]
|
|
1109
|
+
* @property {Record<string, string>} [assets]
|
|
1110
|
+
* @property {string} [hooks]
|
|
1111
|
+
* @property {string[]} [styles]
|
|
1112
|
+
*/
|
|
1113
|
+
/**
|
|
1114
|
+
* @typedef {object} BootConfiguration
|
|
1115
|
+
* @property {string} [route] 路由管理器
|
|
1116
|
+
* @property {string} [theme] 界面主题
|
|
1117
|
+
* @property {string} [authenticator] 登陆器
|
|
1118
|
+
* @property {boolean} [authRequired] 是否强制登陆
|
|
1119
|
+
* @property {string} [skin]
|
|
1120
|
+
* @property {boolean} [single]
|
|
1121
|
+
* @property {string} [application]
|
|
1122
|
+
* @property {string} [domain]
|
|
1123
|
+
* @property {string | number} [port]
|
|
1124
|
+
* @property {string} [path]
|
|
1125
|
+
* @property {Record<string, string>} [pages]
|
|
1126
|
+
* @property {Record<string, string>} [configurationPages]
|
|
1127
|
+
*
|
|
1128
|
+
*
|
|
1129
|
+
* @property {string} [logo]
|
|
1130
|
+
* @property {string} [title]
|
|
1131
|
+
* @property {string} [company]
|
|
1132
|
+
* @property {string} [type]
|
|
1133
|
+
* @property {string} [help] 帮助链接
|
|
1134
|
+
* @property {any[]} [userMenus]
|
|
1135
|
+
*/
|
|
1136
|
+
/**
|
|
1137
|
+
* @typedef {object} Importmap
|
|
1138
|
+
* @property {Record<string, string>} imports
|
|
1139
|
+
* @property {Record<string, Record<string, string>>} scopes
|
|
1140
|
+
*/
|
|
1141
|
+
/**
|
|
1142
|
+
* @typedef {object} Indicator
|
|
1143
|
+
* @property {string} color
|
|
1144
|
+
* @property {string} label
|
|
1145
|
+
* @property {unknown} [filters]
|
|
1146
|
+
*/
|
|
1147
|
+
/**
|
|
1148
|
+
*
|
|
1149
|
+
* @param {*} v
|
|
1150
|
+
* @returns {v is (...p: any[]) => any}
|
|
1151
|
+
*/
|
|
1152
|
+
function isFunction(v) {
|
|
1153
|
+
return typeof v === "function";
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
//#endregion
|
|
1157
|
+
export { Hook, Search_exports as Search, findPluginIdRegex, getPatternFields, isFunction, pluginIdRegex, runPattern, yieldPermissionConstraintFields };
|
|
1158
|
+
//# sourceMappingURL=index.mjs.map
|