@zucker-framework/provider 1.0.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/LICENSE +201 -0
- package/NOTICE +22 -0
- package/THIRD_PARTY_NOTICES +4 -0
- package/dist/index.d.mts +410 -0
- package/dist/index.d.ts +410 -0
- package/dist/index.js +811 -0
- package/dist/index.mjs +765 -0
- package/package.json +39 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,765 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
|
|
4
|
+
// src/provider-registry.ts
|
|
5
|
+
import { Injectable, Logger } from "@nestjs/common";
|
|
6
|
+
import { NotFoundException } from "@zucker-framework/core";
|
|
7
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
8
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
10
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
11
|
+
} else {
|
|
12
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
13
|
+
if (d = decorators[i]) {
|
|
14
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
19
|
+
}
|
|
20
|
+
__name(_ts_decorate, "_ts_decorate");
|
|
21
|
+
var DefaultProviderRegistry = class _DefaultProviderRegistry {
|
|
22
|
+
static {
|
|
23
|
+
__name(this, "DefaultProviderRegistry");
|
|
24
|
+
}
|
|
25
|
+
logger = new Logger(_DefaultProviderRegistry.name);
|
|
26
|
+
providers = /* @__PURE__ */ new Map();
|
|
27
|
+
defaultId = null;
|
|
28
|
+
hooks = [];
|
|
29
|
+
register(provider) {
|
|
30
|
+
this.providers.set(provider.id, provider);
|
|
31
|
+
this.hooks.forEach((h) => h.onRegister?.(provider));
|
|
32
|
+
this.logger.log(`Provider registered: ${provider.id}`);
|
|
33
|
+
if (this.providers.size === 1) {
|
|
34
|
+
this.defaultId = provider.id;
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
dispose: /* @__PURE__ */ __name(() => this.unregister(provider.id), "dispose")
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
unregister(id) {
|
|
41
|
+
const provider = this.providers.get(id);
|
|
42
|
+
if (provider) {
|
|
43
|
+
this.providers.delete(id);
|
|
44
|
+
this.hooks.forEach((h) => h.onUnregister?.(provider));
|
|
45
|
+
this.logger.log(`Provider unregistered: ${id}`);
|
|
46
|
+
if (this.defaultId === id) {
|
|
47
|
+
this.defaultId = this.providers.size > 0 ? this.providers.keys().next().value ?? null : null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
get(id) {
|
|
52
|
+
return this.providers.get(id);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Get provider by id; throws if not found.
|
|
56
|
+
* Mirrors Java SimpleProvider.getNow() which throws BusinessException.
|
|
57
|
+
*/
|
|
58
|
+
getNow(id) {
|
|
59
|
+
const provider = this.providers.get(id);
|
|
60
|
+
if (!provider) {
|
|
61
|
+
throw new NotFoundException(`Unsupported provider: ${id}`);
|
|
62
|
+
}
|
|
63
|
+
return provider;
|
|
64
|
+
}
|
|
65
|
+
getAll() {
|
|
66
|
+
return Array.from(this.providers.values());
|
|
67
|
+
}
|
|
68
|
+
getDefault() {
|
|
69
|
+
return this.defaultId ? this.providers.get(this.defaultId) : void 0;
|
|
70
|
+
}
|
|
71
|
+
setDefault(id) {
|
|
72
|
+
if (!this.providers.has(id)) {
|
|
73
|
+
throw new NotFoundException(`Provider not found: ${id}`);
|
|
74
|
+
}
|
|
75
|
+
this.defaultId = id;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Register if absent — if a provider with the same id already exists,
|
|
79
|
+
* returns the old provider and skips registration. Otherwise registers
|
|
80
|
+
* the new provider and returns undefined.
|
|
81
|
+
* Mirrors Java SimpleProvider.registerIfAbsent(id, T).
|
|
82
|
+
*/
|
|
83
|
+
registerIfAbsent(provider) {
|
|
84
|
+
const existing = this.providers.get(provider.id);
|
|
85
|
+
if (existing) {
|
|
86
|
+
return existing;
|
|
87
|
+
}
|
|
88
|
+
this.register(provider);
|
|
89
|
+
return void 0;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Register if absent using a builder function.
|
|
93
|
+
* Mirrors Java SimpleProvider.registerIfAbsent(id, Function<String, T>).
|
|
94
|
+
*/
|
|
95
|
+
registerIfAbsentWith(id, builder) {
|
|
96
|
+
const existing = this.providers.get(id);
|
|
97
|
+
if (existing) {
|
|
98
|
+
return existing;
|
|
99
|
+
}
|
|
100
|
+
const provider = builder(id);
|
|
101
|
+
this.register(provider);
|
|
102
|
+
return void 0;
|
|
103
|
+
}
|
|
104
|
+
addHook(hook) {
|
|
105
|
+
this.hooks.push(hook);
|
|
106
|
+
return {
|
|
107
|
+
dispose: /* @__PURE__ */ __name(() => this.removeHook(hook), "dispose")
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
removeHook(hook) {
|
|
111
|
+
const idx = this.hooks.indexOf(hook);
|
|
112
|
+
if (idx >= 0) this.hooks.splice(idx, 1);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
DefaultProviderRegistry = _ts_decorate([
|
|
116
|
+
Injectable()
|
|
117
|
+
], DefaultProviderRegistry);
|
|
118
|
+
|
|
119
|
+
// src/strategy-manager.ts
|
|
120
|
+
import { Injectable as Injectable2, Logger as Logger2 } from "@nestjs/common";
|
|
121
|
+
import { NotFoundException as NotFoundException2 } from "@zucker-framework/core";
|
|
122
|
+
function _ts_decorate2(decorators, target, key, desc) {
|
|
123
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
124
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
125
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
126
|
+
} else {
|
|
127
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
128
|
+
if (d = decorators[i]) {
|
|
129
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
134
|
+
}
|
|
135
|
+
__name(_ts_decorate2, "_ts_decorate");
|
|
136
|
+
var DefaultStrategyManager = class _DefaultStrategyManager {
|
|
137
|
+
static {
|
|
138
|
+
__name(this, "DefaultStrategyManager");
|
|
139
|
+
}
|
|
140
|
+
logger = new Logger2(_DefaultStrategyManager.name);
|
|
141
|
+
strategies = /* @__PURE__ */ new Map();
|
|
142
|
+
register(strategy) {
|
|
143
|
+
this.strategies.set(strategy.id, strategy);
|
|
144
|
+
this.logger.log(`Strategy registered: ${strategy.id}`);
|
|
145
|
+
}
|
|
146
|
+
unregister(id) {
|
|
147
|
+
this.strategies.delete(id);
|
|
148
|
+
this.logger.log(`Strategy unregistered: ${id}`);
|
|
149
|
+
}
|
|
150
|
+
get(id) {
|
|
151
|
+
return this.strategies.get(id);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Get strategy by id; throws if not found.
|
|
155
|
+
* Mirrors Java StaticStrategyManager's implicit throw behaviour via
|
|
156
|
+
* switchIfEmpty + onStrategyNotFound.
|
|
157
|
+
*/
|
|
158
|
+
getNow(id) {
|
|
159
|
+
const strategy = this.strategies.get(id);
|
|
160
|
+
if (!strategy) {
|
|
161
|
+
return this.onStrategyNotFound(id);
|
|
162
|
+
}
|
|
163
|
+
return strategy;
|
|
164
|
+
}
|
|
165
|
+
getAll() {
|
|
166
|
+
return Array.from(this.strategies.values());
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Check if a strategy is registered.
|
|
170
|
+
*/
|
|
171
|
+
has(id) {
|
|
172
|
+
return this.strategies.has(id);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Execute an async function with a resolved strategy.
|
|
176
|
+
* Mirrors Java StaticStrategyManager.doWithMono().
|
|
177
|
+
*/
|
|
178
|
+
async doWith(strategyId, executor) {
|
|
179
|
+
const strategy = this.get(strategyId);
|
|
180
|
+
if (!strategy) {
|
|
181
|
+
try {
|
|
182
|
+
this.onStrategyNotFound(strategyId);
|
|
183
|
+
} catch (err) {
|
|
184
|
+
this.logger.debug(`Strategy not found (doWith): ${strategyId} \u2014 ${err instanceof Error ? err.message : String(err)}`);
|
|
185
|
+
return void 0;
|
|
186
|
+
}
|
|
187
|
+
return void 0;
|
|
188
|
+
}
|
|
189
|
+
return executor(strategy);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Execute an async function that returns an array with a resolved strategy.
|
|
193
|
+
* Mirrors Java StaticStrategyManager.doWithFlux().
|
|
194
|
+
*/
|
|
195
|
+
async doWithMany(strategyId, executor) {
|
|
196
|
+
const strategy = this.get(strategyId);
|
|
197
|
+
if (!strategy) {
|
|
198
|
+
try {
|
|
199
|
+
this.onStrategyNotFound(strategyId);
|
|
200
|
+
} catch (err) {
|
|
201
|
+
this.logger.debug(`Strategy not found (doWithMany): ${strategyId} \u2014 ${err instanceof Error ? err.message : String(err)}`);
|
|
202
|
+
return [];
|
|
203
|
+
}
|
|
204
|
+
return [];
|
|
205
|
+
}
|
|
206
|
+
return executor(strategy);
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Called when a strategy is not found. By default throws an error.
|
|
210
|
+
* Subclasses can override to return a fallback or silently return undefined.
|
|
211
|
+
* Mirrors Java StaticStrategyManager.onStrategyNotFound().
|
|
212
|
+
*/
|
|
213
|
+
onStrategyNotFound(strategyId) {
|
|
214
|
+
throw new NotFoundException2(`Strategy not found: ${strategyId}`);
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
DefaultStrategyManager = _ts_decorate2([
|
|
218
|
+
Injectable2()
|
|
219
|
+
], DefaultStrategyManager);
|
|
220
|
+
|
|
221
|
+
// src/hook.ts
|
|
222
|
+
var HookManager = class {
|
|
223
|
+
static {
|
|
224
|
+
__name(this, "HookManager");
|
|
225
|
+
}
|
|
226
|
+
hooks = [];
|
|
227
|
+
/**
|
|
228
|
+
* Add a lifecycle hook.
|
|
229
|
+
* @param hook - The hook to add
|
|
230
|
+
*/
|
|
231
|
+
addHook(hook) {
|
|
232
|
+
this.hooks.push(hook);
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Remove a previously added lifecycle hook.
|
|
236
|
+
* @param hook - The hook to remove
|
|
237
|
+
*/
|
|
238
|
+
removeHook(hook) {
|
|
239
|
+
const idx = this.hooks.indexOf(hook);
|
|
240
|
+
if (idx >= 0) this.hooks.splice(idx, 1);
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Fire the onRegister event on all hooks.
|
|
244
|
+
* @param item - The item being registered
|
|
245
|
+
*/
|
|
246
|
+
async fireRegister(item) {
|
|
247
|
+
for (const hook of this.hooks) {
|
|
248
|
+
await hook.onRegister?.(item);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Fire the onUnregister event on all hooks.
|
|
253
|
+
* @param item - The item being unregistered
|
|
254
|
+
*/
|
|
255
|
+
async fireUnregister(item) {
|
|
256
|
+
for (const hook of this.hooks) {
|
|
257
|
+
await hook.onUnregister?.(item);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Get a copy of all registered hooks.
|
|
262
|
+
* @returns An array of registered lifecycle hooks
|
|
263
|
+
*/
|
|
264
|
+
getHooks() {
|
|
265
|
+
return [
|
|
266
|
+
...this.hooks
|
|
267
|
+
];
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
// src/composite-provider.ts
|
|
272
|
+
import { Logger as Logger3 } from "@nestjs/common";
|
|
273
|
+
var CompositeProvider = class _CompositeProvider {
|
|
274
|
+
static {
|
|
275
|
+
__name(this, "CompositeProvider");
|
|
276
|
+
}
|
|
277
|
+
logger = new Logger3(_CompositeProvider.name);
|
|
278
|
+
providers = [];
|
|
279
|
+
constructor(providers) {
|
|
280
|
+
if (providers) {
|
|
281
|
+
this.providers = [
|
|
282
|
+
...providers
|
|
283
|
+
];
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* 添加一个 provider(追加到末尾,优先级最低)
|
|
288
|
+
*/
|
|
289
|
+
add(provider) {
|
|
290
|
+
this.providers.push(provider);
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* 在指定位置插入 provider(索引越小优先级越高)
|
|
294
|
+
*/
|
|
295
|
+
addFirst(provider) {
|
|
296
|
+
this.providers.unshift(provider);
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* 按优先级依次尝试所有 provider,返回首个非空结果(switchIfEmpty 语义)
|
|
300
|
+
*/
|
|
301
|
+
async get(fn) {
|
|
302
|
+
for (const provider of this.providers) {
|
|
303
|
+
try {
|
|
304
|
+
const result = await fn(provider);
|
|
305
|
+
if (result !== void 0 && result !== null) {
|
|
306
|
+
return result;
|
|
307
|
+
}
|
|
308
|
+
} catch (err) {
|
|
309
|
+
this.logger.debug(`Provider attempt failed, trying next: ${err}`);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return void 0;
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* 合并所有 provider 的结果并去重
|
|
316
|
+
* @param fn 从每个 provider 获取数组结果
|
|
317
|
+
* @param keyFn 可选的去重 key 提取函数
|
|
318
|
+
*/
|
|
319
|
+
async getAll(fn, keyFn) {
|
|
320
|
+
const allResults = [];
|
|
321
|
+
const seenKeys = /* @__PURE__ */ new Set();
|
|
322
|
+
for (const provider of this.providers) {
|
|
323
|
+
try {
|
|
324
|
+
const results = await fn(provider);
|
|
325
|
+
for (const item of results) {
|
|
326
|
+
if (keyFn) {
|
|
327
|
+
const key = keyFn(item);
|
|
328
|
+
if (!seenKeys.has(key)) {
|
|
329
|
+
seenKeys.add(key);
|
|
330
|
+
allResults.push(item);
|
|
331
|
+
}
|
|
332
|
+
} else {
|
|
333
|
+
allResults.push(item);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
} catch (err) {
|
|
337
|
+
this.logger.debug(`Provider getAll failed for one source: ${err}`);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
return allResults;
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* 按优先级执行直到成功(switchIfEmpty 语义)
|
|
344
|
+
* 如果某个 provider 抛错则跳过,尝试下一个
|
|
345
|
+
*/
|
|
346
|
+
async execute(fn) {
|
|
347
|
+
let lastError;
|
|
348
|
+
for (const provider of this.providers) {
|
|
349
|
+
try {
|
|
350
|
+
return await fn(provider);
|
|
351
|
+
} catch (err) {
|
|
352
|
+
lastError = err instanceof Error ? err : new Error(String(err));
|
|
353
|
+
this.logger.debug(`Provider execute failed, trying next: ${lastError.message}`);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
throw lastError ?? new Error("No providers available");
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* 移除指定 provider
|
|
360
|
+
* @returns true if the provider was found and removed
|
|
361
|
+
*/
|
|
362
|
+
remove(provider) {
|
|
363
|
+
const idx = this.providers.indexOf(provider);
|
|
364
|
+
if (idx >= 0) {
|
|
365
|
+
this.providers.splice(idx, 1);
|
|
366
|
+
return true;
|
|
367
|
+
}
|
|
368
|
+
return false;
|
|
369
|
+
}
|
|
370
|
+
getProviders() {
|
|
371
|
+
return this.providers;
|
|
372
|
+
}
|
|
373
|
+
get size() {
|
|
374
|
+
return this.providers.length;
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
// src/fallback-registry.ts
|
|
379
|
+
import { Injectable as Injectable3, Logger as Logger4 } from "@nestjs/common";
|
|
380
|
+
function _ts_decorate3(decorators, target, key, desc) {
|
|
381
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
382
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
383
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
384
|
+
} else {
|
|
385
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
386
|
+
if (d = decorators[i]) {
|
|
387
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
392
|
+
}
|
|
393
|
+
__name(_ts_decorate3, "_ts_decorate");
|
|
394
|
+
var FallbackProviderRegistry = class _FallbackProviderRegistry {
|
|
395
|
+
static {
|
|
396
|
+
__name(this, "FallbackProviderRegistry");
|
|
397
|
+
}
|
|
398
|
+
logger = new Logger4(_FallbackProviderRegistry.name);
|
|
399
|
+
delegate = new DefaultProviderRegistry();
|
|
400
|
+
register(provider) {
|
|
401
|
+
return this.delegate.register(provider);
|
|
402
|
+
}
|
|
403
|
+
unregister(id) {
|
|
404
|
+
this.delegate.unregister(id);
|
|
405
|
+
}
|
|
406
|
+
get(id) {
|
|
407
|
+
return this.delegate.get(id);
|
|
408
|
+
}
|
|
409
|
+
getNow(id) {
|
|
410
|
+
return this.delegate.getNow(id);
|
|
411
|
+
}
|
|
412
|
+
registerIfAbsent(provider) {
|
|
413
|
+
return this.delegate.registerIfAbsent(provider);
|
|
414
|
+
}
|
|
415
|
+
registerIfAbsentWith(id, builder) {
|
|
416
|
+
return this.delegate.registerIfAbsentWith(id, builder);
|
|
417
|
+
}
|
|
418
|
+
getAll() {
|
|
419
|
+
return this.delegate.getAll();
|
|
420
|
+
}
|
|
421
|
+
getDefault() {
|
|
422
|
+
return this.delegate.getDefault();
|
|
423
|
+
}
|
|
424
|
+
setDefault(id) {
|
|
425
|
+
this.delegate.setDefault(id);
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* 按序查找 provider:先找主 ID,找不到则依次尝试 fallback ID 列表
|
|
429
|
+
*/
|
|
430
|
+
getWithFallback(id, ...fallbackIds) {
|
|
431
|
+
const primary = this.delegate.get(id);
|
|
432
|
+
if (primary) return primary;
|
|
433
|
+
for (const fallbackId of fallbackIds) {
|
|
434
|
+
const fallback = this.delegate.get(fallbackId);
|
|
435
|
+
if (fallback) {
|
|
436
|
+
this.logger.debug(`Provider '${id}' not found, using fallback '${fallbackId}'`);
|
|
437
|
+
return fallback;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
this.logger.debug(`Provider '${id}' and all fallbacks not found`);
|
|
441
|
+
return void 0;
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* 找不到指定 ID 的 provider 时返回默认 provider
|
|
445
|
+
*/
|
|
446
|
+
getOrDefault(id) {
|
|
447
|
+
return this.delegate.get(id) ?? this.delegate.getDefault();
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
FallbackProviderRegistry = _ts_decorate3([
|
|
451
|
+
Injectable3()
|
|
452
|
+
], FallbackProviderRegistry);
|
|
453
|
+
|
|
454
|
+
// src/provider.module.ts
|
|
455
|
+
import { Module, Global } from "@nestjs/common";
|
|
456
|
+
function _ts_decorate4(decorators, target, key, desc) {
|
|
457
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
458
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
459
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
460
|
+
} else {
|
|
461
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
462
|
+
if (d = decorators[i]) {
|
|
463
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
468
|
+
}
|
|
469
|
+
__name(_ts_decorate4, "_ts_decorate");
|
|
470
|
+
var PROVIDER_REGISTRY = /* @__PURE__ */ Symbol("PROVIDER_REGISTRY");
|
|
471
|
+
var STRATEGY_MANAGER = /* @__PURE__ */ Symbol("STRATEGY_MANAGER");
|
|
472
|
+
var ZuckerProviderModule = class {
|
|
473
|
+
static {
|
|
474
|
+
__name(this, "ZuckerProviderModule");
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
ZuckerProviderModule = _ts_decorate4([
|
|
478
|
+
Global(),
|
|
479
|
+
Module({
|
|
480
|
+
providers: [
|
|
481
|
+
{
|
|
482
|
+
provide: PROVIDER_REGISTRY,
|
|
483
|
+
useClass: DefaultProviderRegistry
|
|
484
|
+
},
|
|
485
|
+
{
|
|
486
|
+
provide: STRATEGY_MANAGER,
|
|
487
|
+
useClass: DefaultStrategyManager
|
|
488
|
+
}
|
|
489
|
+
],
|
|
490
|
+
exports: [
|
|
491
|
+
PROVIDER_REGISTRY,
|
|
492
|
+
STRATEGY_MANAGER
|
|
493
|
+
]
|
|
494
|
+
})
|
|
495
|
+
], ZuckerProviderModule);
|
|
496
|
+
|
|
497
|
+
// src/resource-manager.ts
|
|
498
|
+
import { Injectable as Injectable4, Logger as Logger5 } from "@nestjs/common";
|
|
499
|
+
import { NotFoundException as NotFoundException3 } from "@zucker-framework/core";
|
|
500
|
+
function _ts_decorate5(decorators, target, key, desc) {
|
|
501
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
502
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
503
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
504
|
+
} else {
|
|
505
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
506
|
+
if (d = decorators[i]) {
|
|
507
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
512
|
+
}
|
|
513
|
+
__name(_ts_decorate5, "_ts_decorate");
|
|
514
|
+
var ResourceManager = class _ResourceManager {
|
|
515
|
+
static {
|
|
516
|
+
__name(this, "ResourceManager");
|
|
517
|
+
}
|
|
518
|
+
logger = new Logger5(_ResourceManager.name);
|
|
519
|
+
providers = /* @__PURE__ */ new Map();
|
|
520
|
+
/** 注册资源提供者 */
|
|
521
|
+
registerProvider(provider) {
|
|
522
|
+
this.providers.set(provider.getType(), provider);
|
|
523
|
+
this.logger.log(`Resource provider registered: ${provider.getType()}`);
|
|
524
|
+
}
|
|
525
|
+
/** 注销资源提供者 */
|
|
526
|
+
unregisterProvider(type) {
|
|
527
|
+
this.providers.delete(type);
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* 根据类型和 ID 获取资源(不存在时返回 undefined)
|
|
531
|
+
* @param type 资源类型
|
|
532
|
+
* @param id 资源 ID
|
|
533
|
+
* @returns 资源实例或 undefined
|
|
534
|
+
*/
|
|
535
|
+
async getResource(type, id) {
|
|
536
|
+
const provider = this.providers.get(type);
|
|
537
|
+
if (!provider) return void 0;
|
|
538
|
+
return provider.getResource(id);
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* 根据类型和 ID 获取资源(不存在时抛出 NotFoundException)
|
|
542
|
+
* @param type 资源类型
|
|
543
|
+
* @param id 资源 ID
|
|
544
|
+
* @returns 资源实例
|
|
545
|
+
* @throws NotFoundException 资源不存在时
|
|
546
|
+
*/
|
|
547
|
+
async getResourceOrThrow(type, id) {
|
|
548
|
+
const resource = await this.getResource(type, id);
|
|
549
|
+
if (resource === void 0 || resource === null) {
|
|
550
|
+
throw new NotFoundException3(`Resource not found: ${type}/${id}`);
|
|
551
|
+
}
|
|
552
|
+
return resource;
|
|
553
|
+
}
|
|
554
|
+
/** 获取指定类型的资源提供者 */
|
|
555
|
+
getProvider(type) {
|
|
556
|
+
return this.providers.get(type);
|
|
557
|
+
}
|
|
558
|
+
/** 获取所有已注册的资源类型 */
|
|
559
|
+
getTypes() {
|
|
560
|
+
return Array.from(this.providers.keys());
|
|
561
|
+
}
|
|
562
|
+
};
|
|
563
|
+
ResourceManager = _ts_decorate5([
|
|
564
|
+
Injectable4()
|
|
565
|
+
], ResourceManager);
|
|
566
|
+
|
|
567
|
+
// src/classpath-resource-provider.ts
|
|
568
|
+
import * as fs from "fs";
|
|
569
|
+
import * as path from "path";
|
|
570
|
+
var ClasspathResourceProvider = class {
|
|
571
|
+
static {
|
|
572
|
+
__name(this, "ClasspathResourceProvider");
|
|
573
|
+
}
|
|
574
|
+
type;
|
|
575
|
+
basePath;
|
|
576
|
+
constructor(type, basePath) {
|
|
577
|
+
this.type = type;
|
|
578
|
+
this.basePath = basePath;
|
|
579
|
+
}
|
|
580
|
+
getType() {
|
|
581
|
+
return this.type;
|
|
582
|
+
}
|
|
583
|
+
async getResource(id) {
|
|
584
|
+
const filePath = path.join(this.basePath, `${id}.json`);
|
|
585
|
+
const resolved = path.resolve(filePath);
|
|
586
|
+
const baseResolved = path.resolve(this.basePath);
|
|
587
|
+
if (!resolved.startsWith(baseResolved + path.sep) && resolved !== baseResolved) {
|
|
588
|
+
throw new Error(`Path traversal detected: "${id}"`);
|
|
589
|
+
}
|
|
590
|
+
if (!fs.existsSync(filePath)) return void 0;
|
|
591
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
592
|
+
return JSON.parse(content);
|
|
593
|
+
}
|
|
594
|
+
async listResources() {
|
|
595
|
+
if (!fs.existsSync(this.basePath)) return [];
|
|
596
|
+
return fs.readdirSync(this.basePath).filter((f) => f.endsWith(".json")).map((f) => path.basename(f, ".json"));
|
|
597
|
+
}
|
|
598
|
+
};
|
|
599
|
+
|
|
600
|
+
// src/file-resource-provider.ts
|
|
601
|
+
import * as fs2 from "fs";
|
|
602
|
+
import * as path2 from "path";
|
|
603
|
+
var FileResourceProvider = class {
|
|
604
|
+
static {
|
|
605
|
+
__name(this, "FileResourceProvider");
|
|
606
|
+
}
|
|
607
|
+
type;
|
|
608
|
+
basePath;
|
|
609
|
+
extension;
|
|
610
|
+
constructor(type, basePath, extension = "") {
|
|
611
|
+
this.type = type;
|
|
612
|
+
this.basePath = basePath;
|
|
613
|
+
this.extension = extension;
|
|
614
|
+
}
|
|
615
|
+
getType() {
|
|
616
|
+
return this.type;
|
|
617
|
+
}
|
|
618
|
+
async getResource(id) {
|
|
619
|
+
const filePath = path2.join(this.basePath, this.extension ? `${id}.${this.extension}` : id);
|
|
620
|
+
const resolved = path2.resolve(filePath);
|
|
621
|
+
if (!resolved.startsWith(path2.resolve(this.basePath))) {
|
|
622
|
+
throw new Error("Path traversal detected");
|
|
623
|
+
}
|
|
624
|
+
if (!fs2.existsSync(resolved)) return void 0;
|
|
625
|
+
return fs2.readFileSync(resolved);
|
|
626
|
+
}
|
|
627
|
+
async listResources() {
|
|
628
|
+
if (!fs2.existsSync(this.basePath)) return [];
|
|
629
|
+
return fs2.readdirSync(this.basePath).map((f) => {
|
|
630
|
+
if (this.extension) {
|
|
631
|
+
return path2.basename(f, `.${this.extension}`);
|
|
632
|
+
}
|
|
633
|
+
return f;
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
};
|
|
637
|
+
|
|
638
|
+
// src/health-check.ts
|
|
639
|
+
import { Logger as Logger6 } from "@nestjs/common";
|
|
640
|
+
var ProviderHealthManager = class _ProviderHealthManager {
|
|
641
|
+
static {
|
|
642
|
+
__name(this, "ProviderHealthManager");
|
|
643
|
+
}
|
|
644
|
+
registry;
|
|
645
|
+
logger = new Logger6(_ProviderHealthManager.name);
|
|
646
|
+
healthState = /* @__PURE__ */ new Map();
|
|
647
|
+
intervalHandle;
|
|
648
|
+
constructor(registry) {
|
|
649
|
+
this.registry = registry;
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* 对所有已注册的 Provider 执行健康检查
|
|
653
|
+
*/
|
|
654
|
+
async checkAll() {
|
|
655
|
+
const providers = this.registry.getAll();
|
|
656
|
+
const results = [];
|
|
657
|
+
for (const provider of providers) {
|
|
658
|
+
const info = await this.check(provider);
|
|
659
|
+
results.push(info);
|
|
660
|
+
}
|
|
661
|
+
return results;
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* 对单个 Provider 执行健康检查
|
|
665
|
+
*/
|
|
666
|
+
async check(provider) {
|
|
667
|
+
const existing = this.healthState.get(provider.id) ?? {
|
|
668
|
+
id: provider.id,
|
|
669
|
+
status: "unknown",
|
|
670
|
+
lastChecked: 0,
|
|
671
|
+
consecutiveFailures: 0,
|
|
672
|
+
metadata: provider.metadata
|
|
673
|
+
};
|
|
674
|
+
const instance = provider.instance;
|
|
675
|
+
if (!this.isHealthCheckable(instance)) {
|
|
676
|
+
existing.status = "unknown";
|
|
677
|
+
existing.lastChecked = Date.now();
|
|
678
|
+
this.healthState.set(provider.id, existing);
|
|
679
|
+
return {
|
|
680
|
+
...existing
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
try {
|
|
684
|
+
const healthy = await instance.healthCheck();
|
|
685
|
+
existing.status = healthy ? "healthy" : "unhealthy";
|
|
686
|
+
existing.consecutiveFailures = healthy ? 0 : existing.consecutiveFailures + 1;
|
|
687
|
+
} catch (error) {
|
|
688
|
+
existing.status = "unhealthy";
|
|
689
|
+
existing.consecutiveFailures += 1;
|
|
690
|
+
this.logger.warn(`Health check failed for provider "${provider.id}": ${error instanceof Error ? error.message : error}`);
|
|
691
|
+
}
|
|
692
|
+
existing.lastChecked = Date.now();
|
|
693
|
+
this.healthState.set(provider.id, existing);
|
|
694
|
+
return {
|
|
695
|
+
...existing
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* 获取所有 Provider 的健康状态
|
|
700
|
+
*/
|
|
701
|
+
getHealthStatus() {
|
|
702
|
+
return Array.from(this.healthState.values());
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* 获取只包含健康 Provider 的列表
|
|
706
|
+
*/
|
|
707
|
+
getHealthyProviders() {
|
|
708
|
+
return this.registry.getAll().filter((p) => {
|
|
709
|
+
const info = this.healthState.get(p.id);
|
|
710
|
+
return !info || info.status !== "unhealthy";
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* 启动定期健康检查
|
|
715
|
+
*/
|
|
716
|
+
startPeriodicChecks(options) {
|
|
717
|
+
this.stop();
|
|
718
|
+
this.intervalHandle = setInterval(() => {
|
|
719
|
+
this.checkAll().catch((err) => {
|
|
720
|
+
this.logger.error(`Periodic health check failed: ${err}`);
|
|
721
|
+
});
|
|
722
|
+
}, options.intervalMs);
|
|
723
|
+
}
|
|
724
|
+
/**
|
|
725
|
+
* 停止定期健康检查
|
|
726
|
+
*/
|
|
727
|
+
stop() {
|
|
728
|
+
if (this.intervalHandle) {
|
|
729
|
+
clearInterval(this.intervalHandle);
|
|
730
|
+
this.intervalHandle = void 0;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
/**
|
|
734
|
+
* 检查 Provider 是否应该被激活(条件激活)
|
|
735
|
+
*/
|
|
736
|
+
async shouldActivate(provider) {
|
|
737
|
+
const instance = provider.instance;
|
|
738
|
+
if (!this.isConditional(instance)) return true;
|
|
739
|
+
try {
|
|
740
|
+
return await instance.shouldActivate();
|
|
741
|
+
} catch {
|
|
742
|
+
return false;
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
isHealthCheckable(instance) {
|
|
746
|
+
return typeof instance === "object" && instance !== null && "healthCheck" in instance && typeof instance.healthCheck === "function";
|
|
747
|
+
}
|
|
748
|
+
isConditional(instance) {
|
|
749
|
+
return typeof instance === "object" && instance !== null && "shouldActivate" in instance && typeof instance.shouldActivate === "function";
|
|
750
|
+
}
|
|
751
|
+
};
|
|
752
|
+
export {
|
|
753
|
+
ClasspathResourceProvider,
|
|
754
|
+
CompositeProvider,
|
|
755
|
+
DefaultProviderRegistry,
|
|
756
|
+
DefaultStrategyManager,
|
|
757
|
+
FallbackProviderRegistry,
|
|
758
|
+
FileResourceProvider,
|
|
759
|
+
HookManager,
|
|
760
|
+
PROVIDER_REGISTRY,
|
|
761
|
+
ProviderHealthManager,
|
|
762
|
+
ResourceManager,
|
|
763
|
+
STRATEGY_MANAGER,
|
|
764
|
+
ZuckerProviderModule
|
|
765
|
+
};
|