@b9g/platform 0.1.11 → 0.1.13
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/README.md +1 -1
- package/package.json +22 -38
- package/src/config.d.ts +15 -163
- package/src/config.js +18 -630
- package/src/globals.d.ts +119 -0
- package/src/index.d.ts +294 -25
- package/src/index.js +466 -126
- package/src/runtime.d.ts +423 -22
- package/src/runtime.js +693 -250
- package/src/shovel-config.d.ts +10 -0
- package/chunk-P57PW2II.js +0 -11
- package/src/cookie-store.d.ts +0 -80
- package/src/cookie-store.js +0 -233
- package/src/single-threaded.d.ts +0 -59
- package/src/single-threaded.js +0 -114
- package/src/worker-pool.d.ts +0 -93
- package/src/worker-pool.js +0 -390
package/src/config.js
CHANGED
|
@@ -1,641 +1,29 @@
|
|
|
1
1
|
/// <reference types="./config.d.ts" />
|
|
2
|
-
import "../chunk-P57PW2II.js";
|
|
3
|
-
|
|
4
2
|
// src/config.ts
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
if (typeof process !== "undefined" && process.env) {
|
|
14
|
-
return process.env;
|
|
15
|
-
}
|
|
16
|
-
return {};
|
|
17
|
-
}
|
|
18
|
-
var Tokenizer = class {
|
|
19
|
-
#input;
|
|
20
|
-
#pos;
|
|
21
|
-
constructor(input) {
|
|
22
|
-
this.#input = input;
|
|
23
|
-
this.#pos = 0;
|
|
24
|
-
}
|
|
25
|
-
#peek() {
|
|
26
|
-
return this.#input[this.#pos] || "";
|
|
27
|
-
}
|
|
28
|
-
#advance() {
|
|
29
|
-
return this.#input[this.#pos++] || "";
|
|
30
|
-
}
|
|
31
|
-
#skipWhitespace() {
|
|
32
|
-
while (/\s/.test(this.#peek())) {
|
|
33
|
-
this.#advance();
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
next() {
|
|
37
|
-
this.#skipWhitespace();
|
|
38
|
-
const start = this.#pos;
|
|
39
|
-
const ch = this.#peek();
|
|
40
|
-
if (!ch) {
|
|
41
|
-
return { type: "EOF" /* EOF */, value: null, start, end: start };
|
|
42
|
-
}
|
|
43
|
-
if (ch === '"') {
|
|
44
|
-
this.#advance();
|
|
45
|
-
let value = "";
|
|
46
|
-
while (this.#peek() && this.#peek() !== '"') {
|
|
47
|
-
if (this.#peek() === "\\") {
|
|
48
|
-
this.#advance();
|
|
49
|
-
const next = this.#advance();
|
|
50
|
-
if (next === "n")
|
|
51
|
-
value += "\n";
|
|
52
|
-
else if (next === "t")
|
|
53
|
-
value += " ";
|
|
54
|
-
else
|
|
55
|
-
value += next;
|
|
56
|
-
} else {
|
|
57
|
-
value += this.#advance();
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
if (this.#peek() !== '"') {
|
|
61
|
-
throw new Error(`Unterminated string at position ${start}`);
|
|
62
|
-
}
|
|
63
|
-
this.#advance();
|
|
64
|
-
return { type: "STRING" /* STRING */, value, start, end: this.#pos };
|
|
65
|
-
}
|
|
66
|
-
if (/\d/.test(ch)) {
|
|
67
|
-
let value = "";
|
|
68
|
-
while (/\d/.test(this.#peek())) {
|
|
69
|
-
value += this.#advance();
|
|
70
|
-
}
|
|
71
|
-
return {
|
|
72
|
-
type: "NUMBER" /* NUMBER */,
|
|
73
|
-
value: parseInt(value, 10),
|
|
74
|
-
start,
|
|
75
|
-
end: this.#pos
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
if (ch === "=" && this.#input[this.#pos + 1] === "=" && this.#input[this.#pos + 2] === "=") {
|
|
79
|
-
this.#pos += 3;
|
|
80
|
-
return { type: "===" /* EQ_STRICT */, value: "===", start, end: this.#pos };
|
|
81
|
-
}
|
|
82
|
-
if (ch === "!" && this.#input[this.#pos + 1] === "=" && this.#input[this.#pos + 2] === "=") {
|
|
83
|
-
this.#pos += 3;
|
|
84
|
-
return { type: "!==" /* NE_STRICT */, value: "!==", start, end: this.#pos };
|
|
85
|
-
}
|
|
86
|
-
if (ch === "=" && this.#input[this.#pos + 1] === "=") {
|
|
87
|
-
this.#pos += 2;
|
|
88
|
-
return { type: "==" /* EQ */, value: "==", start, end: this.#pos };
|
|
89
|
-
}
|
|
90
|
-
if (ch === "!" && this.#input[this.#pos + 1] === "=") {
|
|
91
|
-
this.#pos += 2;
|
|
92
|
-
return { type: "!=" /* NE */, value: "!=", start, end: this.#pos };
|
|
93
|
-
}
|
|
94
|
-
if (ch === "|" && this.#input[this.#pos + 1] === "|") {
|
|
95
|
-
this.#pos += 2;
|
|
96
|
-
return { type: "||" /* OR */, value: "||", start, end: this.#pos };
|
|
97
|
-
}
|
|
98
|
-
if (ch === "&" && this.#input[this.#pos + 1] === "&") {
|
|
99
|
-
this.#pos += 2;
|
|
100
|
-
return { type: "&&" /* AND */, value: "&&", start, end: this.#pos };
|
|
101
|
-
}
|
|
102
|
-
if (ch === "?") {
|
|
103
|
-
this.#advance();
|
|
104
|
-
return { type: "?" /* QUESTION */, value: "?", start, end: this.#pos };
|
|
105
|
-
}
|
|
106
|
-
if (ch === "!") {
|
|
107
|
-
this.#advance();
|
|
108
|
-
return { type: "!" /* NOT */, value: "!", start, end: this.#pos };
|
|
109
|
-
}
|
|
110
|
-
if (ch === "(") {
|
|
111
|
-
this.#advance();
|
|
112
|
-
return { type: "(" /* LPAREN */, value: "(", start, end: this.#pos };
|
|
113
|
-
}
|
|
114
|
-
if (ch === ")") {
|
|
115
|
-
this.#advance();
|
|
116
|
-
return { type: ")" /* RPAREN */, value: ")", start, end: this.#pos };
|
|
117
|
-
}
|
|
118
|
-
if (ch === ":") {
|
|
119
|
-
const next = this.#input[this.#pos + 1];
|
|
120
|
-
if (next !== "/" && !/\d/.test(next)) {
|
|
121
|
-
this.#advance();
|
|
122
|
-
return { type: ":" /* COLON */, value: ":", start, end: this.#pos };
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
if (/\S/.test(ch) && !/[?!()=|&]/.test(ch)) {
|
|
126
|
-
let value = "";
|
|
127
|
-
while (/\S/.test(this.#peek()) && !/[?!()=|&]/.test(this.#peek())) {
|
|
128
|
-
if (this.#peek() === ":") {
|
|
129
|
-
const next = this.#input[this.#pos + 1];
|
|
130
|
-
if (next !== "/" && !/\d/.test(next)) {
|
|
131
|
-
break;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
value += this.#advance();
|
|
135
|
-
}
|
|
136
|
-
if (value === "true")
|
|
137
|
-
return { type: "TRUE" /* TRUE */, value: true, start, end: this.#pos };
|
|
138
|
-
if (value === "false")
|
|
139
|
-
return { type: "FALSE" /* FALSE */, value: false, start, end: this.#pos };
|
|
140
|
-
if (value === "null")
|
|
141
|
-
return { type: "NULL" /* NULL */, value: null, start, end: this.#pos };
|
|
142
|
-
if (value === "undefined")
|
|
143
|
-
return {
|
|
144
|
-
type: "UNDEFINED" /* UNDEFINED */,
|
|
145
|
-
value: void 0,
|
|
146
|
-
start,
|
|
147
|
-
end: this.#pos
|
|
148
|
-
};
|
|
149
|
-
return { type: "IDENTIFIER" /* IDENTIFIER */, value, start, end: this.#pos };
|
|
150
|
-
}
|
|
151
|
-
throw new Error(`Unexpected character '${ch}' at position ${start}`);
|
|
152
|
-
}
|
|
153
|
-
};
|
|
154
|
-
var Parser = class {
|
|
155
|
-
#tokens;
|
|
156
|
-
#pos;
|
|
157
|
-
#env;
|
|
158
|
-
#strict;
|
|
159
|
-
constructor(input, env, strict) {
|
|
160
|
-
const tokenizer = new Tokenizer(input);
|
|
161
|
-
this.#tokens = [];
|
|
162
|
-
let token;
|
|
163
|
-
do {
|
|
164
|
-
token = tokenizer.next();
|
|
165
|
-
this.#tokens.push(token);
|
|
166
|
-
} while (token.type !== "EOF" /* EOF */);
|
|
167
|
-
this.#pos = 0;
|
|
168
|
-
this.#env = env;
|
|
169
|
-
this.#strict = strict;
|
|
170
|
-
}
|
|
171
|
-
#peek() {
|
|
172
|
-
return this.#tokens[this.#pos];
|
|
173
|
-
}
|
|
174
|
-
#advance() {
|
|
175
|
-
return this.#tokens[this.#pos++];
|
|
176
|
-
}
|
|
177
|
-
#expect(type) {
|
|
178
|
-
const token = this.#peek();
|
|
179
|
-
if (token.type !== type) {
|
|
180
|
-
throw new Error(
|
|
181
|
-
`Expected ${type} but got ${token.type} at position ${token.start}`
|
|
182
|
-
);
|
|
183
|
-
}
|
|
184
|
-
return this.#advance();
|
|
185
|
-
}
|
|
186
|
-
parse() {
|
|
187
|
-
const result = this.#parseExpr();
|
|
188
|
-
this.#expect("EOF" /* EOF */);
|
|
189
|
-
return result;
|
|
190
|
-
}
|
|
191
|
-
// Expr := Ternary
|
|
192
|
-
#parseExpr() {
|
|
193
|
-
return this.#parseTernary();
|
|
194
|
-
}
|
|
195
|
-
// Ternary := LogicalOr ('?' Expr ':' Expr)?
|
|
196
|
-
#parseTernary() {
|
|
197
|
-
let left = this.#parseLogicalOr();
|
|
198
|
-
if (this.#peek().type === "?" /* QUESTION */) {
|
|
199
|
-
this.#advance();
|
|
200
|
-
const trueBranch = this.#parseExpr();
|
|
201
|
-
this.#expect(":" /* COLON */);
|
|
202
|
-
const falseBranch = this.#parseExpr();
|
|
203
|
-
return left ? trueBranch : falseBranch;
|
|
204
|
-
}
|
|
205
|
-
return left;
|
|
206
|
-
}
|
|
207
|
-
// LogicalOr := LogicalAnd ('||' LogicalAnd)*
|
|
208
|
-
#parseLogicalOr() {
|
|
209
|
-
let left = this.#parseLogicalAnd();
|
|
210
|
-
while (this.#peek().type === "||" /* OR */) {
|
|
211
|
-
this.#advance();
|
|
212
|
-
const right = this.#parseLogicalAnd();
|
|
213
|
-
left = left || right;
|
|
214
|
-
}
|
|
215
|
-
return left;
|
|
216
|
-
}
|
|
217
|
-
// LogicalAnd := Equality ('&&' Equality)*
|
|
218
|
-
#parseLogicalAnd() {
|
|
219
|
-
let left = this.#parseEquality();
|
|
220
|
-
while (this.#peek().type === "&&" /* AND */) {
|
|
221
|
-
this.#advance();
|
|
222
|
-
const right = this.#parseEquality();
|
|
223
|
-
left = left && right;
|
|
224
|
-
}
|
|
225
|
-
return left;
|
|
226
|
-
}
|
|
227
|
-
// Equality := Unary (('===' | '!==' | '==' | '!=') Unary)*
|
|
228
|
-
#parseEquality() {
|
|
229
|
-
let left = this.#parseUnary();
|
|
230
|
-
while (true) {
|
|
231
|
-
const token = this.#peek();
|
|
232
|
-
if (token.type === "===" /* EQ_STRICT */) {
|
|
233
|
-
this.#advance();
|
|
234
|
-
const right = this.#parseUnary();
|
|
235
|
-
left = left === right;
|
|
236
|
-
} else if (token.type === "!==" /* NE_STRICT */) {
|
|
237
|
-
this.#advance();
|
|
238
|
-
const right = this.#parseUnary();
|
|
239
|
-
left = left !== right;
|
|
240
|
-
} else if (token.type === "==" /* EQ */) {
|
|
241
|
-
this.#advance();
|
|
242
|
-
const right = this.#parseUnary();
|
|
243
|
-
left = left == right;
|
|
244
|
-
} else if (token.type === "!=" /* NE */) {
|
|
245
|
-
this.#advance();
|
|
246
|
-
const right = this.#parseUnary();
|
|
247
|
-
left = left != right;
|
|
248
|
-
} else {
|
|
249
|
-
break;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
return left;
|
|
253
|
-
}
|
|
254
|
-
// Unary := '!' Unary | Primary
|
|
255
|
-
#parseUnary() {
|
|
256
|
-
if (this.#peek().type === "!" /* NOT */) {
|
|
257
|
-
this.#advance();
|
|
258
|
-
return !this.#parseUnary();
|
|
259
|
-
}
|
|
260
|
-
return this.#parsePrimary();
|
|
261
|
-
}
|
|
262
|
-
// Primary := EnvVar | Literal | '(' Expr ')'
|
|
263
|
-
#parsePrimary() {
|
|
264
|
-
const token = this.#peek();
|
|
265
|
-
if (token.type === "(" /* LPAREN */) {
|
|
266
|
-
this.#advance();
|
|
267
|
-
const value = this.#parseExpr();
|
|
268
|
-
this.#expect(")" /* RPAREN */);
|
|
269
|
-
return value;
|
|
270
|
-
}
|
|
271
|
-
if (token.type === "STRING" /* STRING */) {
|
|
272
|
-
this.#advance();
|
|
273
|
-
return token.value;
|
|
274
|
-
}
|
|
275
|
-
if (token.type === "NUMBER" /* NUMBER */) {
|
|
276
|
-
this.#advance();
|
|
277
|
-
return token.value;
|
|
278
|
-
}
|
|
279
|
-
if (token.type === "TRUE" /* TRUE */) {
|
|
280
|
-
this.#advance();
|
|
281
|
-
return true;
|
|
282
|
-
}
|
|
283
|
-
if (token.type === "FALSE" /* FALSE */) {
|
|
284
|
-
this.#advance();
|
|
285
|
-
return false;
|
|
286
|
-
}
|
|
287
|
-
if (token.type === "NULL" /* NULL */) {
|
|
288
|
-
this.#advance();
|
|
289
|
-
return null;
|
|
290
|
-
}
|
|
291
|
-
if (token.type === "UNDEFINED" /* UNDEFINED */) {
|
|
292
|
-
this.#advance();
|
|
293
|
-
return void 0;
|
|
294
|
-
}
|
|
295
|
-
if (token.type === "IDENTIFIER" /* IDENTIFIER */) {
|
|
296
|
-
this.#advance();
|
|
297
|
-
const name = token.value;
|
|
298
|
-
if (/^[A-Z][A-Z0-9_]*$/.test(name)) {
|
|
299
|
-
const value = this.#env[name];
|
|
300
|
-
if (this.#strict && value === void 0) {
|
|
301
|
-
throw new Error(
|
|
302
|
-
`Undefined environment variable: ${name}
|
|
303
|
-
Fix:
|
|
304
|
-
1. Set the env var: export ${name}=value
|
|
305
|
-
2. Add a fallback: ${name} || defaultValue
|
|
306
|
-
3. Add null check: ${name} == null ? ... : ...
|
|
307
|
-
4. Use empty string for falsy: export ${name}=""`
|
|
308
|
-
);
|
|
309
|
-
}
|
|
310
|
-
if (typeof value === "string" && /^\d+$/.test(value)) {
|
|
311
|
-
return parseInt(value, 10);
|
|
312
|
-
}
|
|
313
|
-
return value;
|
|
314
|
-
}
|
|
315
|
-
return name;
|
|
316
|
-
}
|
|
317
|
-
throw new Error(
|
|
318
|
-
`Unexpected token ${token.type} at position ${token.start}`
|
|
319
|
-
);
|
|
3
|
+
var ConfigValidationError = class extends Error {
|
|
4
|
+
constructor(path, issue) {
|
|
5
|
+
const message = issue === "undefined" ? `Config "${path}" is undefined. Ensure required environment variables are set.` : `Config "${path}" is NaN. Ensure the environment variable contains a valid number.`;
|
|
6
|
+
super(message);
|
|
7
|
+
this.path = path;
|
|
8
|
+
this.issue = issue;
|
|
9
|
+
this.name = "ConfigValidationError";
|
|
320
10
|
}
|
|
321
11
|
};
|
|
322
|
-
function
|
|
323
|
-
const
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
} catch (error) {
|
|
328
|
-
throw new Error(
|
|
329
|
-
`Invalid config expression: ${expr}
|
|
330
|
-
Error: ${error instanceof Error ? error.message : String(error)}`
|
|
331
|
-
);
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
function processConfigValue(value, env = getEnv(), options = {}) {
|
|
335
|
-
if (typeof value === "string") {
|
|
336
|
-
if (/(\|\||&&|===|!==|==|!=|[?:!]|^[A-Z][A-Z0-9_]*$)/.test(value)) {
|
|
337
|
-
return parseConfigExpr(value, env, options);
|
|
12
|
+
function validateConfig(config, path = "") {
|
|
13
|
+
for (const [key, value] of Object.entries(config)) {
|
|
14
|
+
const fullPath = path ? `${path}.${key}` : key;
|
|
15
|
+
if (value === void 0) {
|
|
16
|
+
throw new ConfigValidationError(fullPath, "undefined");
|
|
338
17
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
if (Array.isArray(value)) {
|
|
342
|
-
return value.map((item) => processConfigValue(item, env, options));
|
|
343
|
-
}
|
|
344
|
-
if (value !== null && typeof value === "object") {
|
|
345
|
-
const processed = {};
|
|
346
|
-
for (const [key, val] of Object.entries(value)) {
|
|
347
|
-
processed[key] = processConfigValue(val, env, options);
|
|
348
|
-
}
|
|
349
|
-
return processed;
|
|
350
|
-
}
|
|
351
|
-
return value;
|
|
352
|
-
}
|
|
353
|
-
function matchPattern(name, config) {
|
|
354
|
-
if (config[name]) {
|
|
355
|
-
return config[name];
|
|
356
|
-
}
|
|
357
|
-
const patterns = [];
|
|
358
|
-
for (const [pattern, cfg] of Object.entries(config)) {
|
|
359
|
-
if (pattern === "*")
|
|
360
|
-
continue;
|
|
361
|
-
if (pattern.endsWith("*")) {
|
|
362
|
-
const prefix = pattern.slice(0, -1);
|
|
363
|
-
if (name.startsWith(prefix)) {
|
|
364
|
-
patterns.push({
|
|
365
|
-
pattern,
|
|
366
|
-
config: cfg,
|
|
367
|
-
prefixLength: prefix.length
|
|
368
|
-
});
|
|
369
|
-
}
|
|
18
|
+
if (typeof value === "number" && Number.isNaN(value)) {
|
|
19
|
+
throw new ConfigValidationError(fullPath, "NaN");
|
|
370
20
|
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
patterns.sort((a, b) => b.prefixLength - a.prefixLength);
|
|
374
|
-
return patterns[0].config;
|
|
375
|
-
}
|
|
376
|
-
return config["*"];
|
|
377
|
-
}
|
|
378
|
-
function loadConfig(cwd) {
|
|
379
|
-
const env = getEnv();
|
|
380
|
-
let rawConfig = {};
|
|
381
|
-
try {
|
|
382
|
-
const shovelPath = `${cwd}/shovel.json`;
|
|
383
|
-
const content = readFileSync(shovelPath, "utf-8");
|
|
384
|
-
rawConfig = JSON.parse(content);
|
|
385
|
-
} catch (error) {
|
|
386
|
-
try {
|
|
387
|
-
const pkgPath = `${cwd}/package.json`;
|
|
388
|
-
const content = readFileSync(pkgPath, "utf-8");
|
|
389
|
-
const pkgJSON = JSON.parse(content);
|
|
390
|
-
rawConfig = pkgJSON.shovel || {};
|
|
391
|
-
} catch (error2) {
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
const processed = processConfigValue(rawConfig, env, {
|
|
395
|
-
strict: true
|
|
396
|
-
});
|
|
397
|
-
const defaultSinks = [{ provider: "console" }];
|
|
398
|
-
const config = {
|
|
399
|
-
platform: processed.platform,
|
|
400
|
-
port: typeof processed.port === "number" ? processed.port : 3e3,
|
|
401
|
-
host: processed.host || "localhost",
|
|
402
|
-
workers: typeof processed.workers === "number" ? processed.workers : 1,
|
|
403
|
-
logging: {
|
|
404
|
-
level: processed.logging?.level || "info",
|
|
405
|
-
sinks: processed.logging?.sinks || defaultSinks,
|
|
406
|
-
categories: processed.logging?.categories || {}
|
|
407
|
-
},
|
|
408
|
-
caches: processed.caches || {},
|
|
409
|
-
buckets: processed.buckets || {}
|
|
410
|
-
};
|
|
411
|
-
return config;
|
|
412
|
-
}
|
|
413
|
-
var SHOVEL_CATEGORIES = [
|
|
414
|
-
"cli",
|
|
415
|
-
"watcher",
|
|
416
|
-
"worker",
|
|
417
|
-
"single-threaded",
|
|
418
|
-
"assets",
|
|
419
|
-
"platform-node",
|
|
420
|
-
"platform-bun",
|
|
421
|
-
"platform-cloudflare",
|
|
422
|
-
"cache",
|
|
423
|
-
"cache-redis",
|
|
424
|
-
"router"
|
|
425
|
-
];
|
|
426
|
-
var BUILTIN_SINK_PROVIDERS = {
|
|
427
|
-
console: { module: "@logtape/logtape", factory: "getConsoleSink" },
|
|
428
|
-
file: { module: "@logtape/file", factory: "getFileSink" },
|
|
429
|
-
rotating: { module: "@logtape/file", factory: "getRotatingFileSink" },
|
|
430
|
-
"stream-file": { module: "@logtape/file", factory: "getStreamFileSink" },
|
|
431
|
-
otel: { module: "@logtape/otel", factory: "getOpenTelemetrySink" },
|
|
432
|
-
sentry: { module: "@logtape/sentry", factory: "getSentrySink" },
|
|
433
|
-
syslog: { module: "@logtape/syslog", factory: "getSyslogSink" },
|
|
434
|
-
cloudwatch: {
|
|
435
|
-
module: "@logtape/cloudwatch-logs",
|
|
436
|
-
factory: "getCloudWatchLogsSink"
|
|
437
|
-
}
|
|
438
|
-
};
|
|
439
|
-
async function createSink(config, options = {}) {
|
|
440
|
-
const { provider, ...sinkOptions } = config;
|
|
441
|
-
if (sinkOptions.path && options.cwd) {
|
|
442
|
-
sinkOptions.path = resolve(options.cwd, sinkOptions.path);
|
|
443
|
-
}
|
|
444
|
-
const builtin = BUILTIN_SINK_PROVIDERS[provider];
|
|
445
|
-
const modulePath = builtin?.module || provider;
|
|
446
|
-
const factoryName = builtin?.factory || "default";
|
|
447
|
-
const module = await import(modulePath);
|
|
448
|
-
const factory = module[factoryName] || module.default;
|
|
449
|
-
if (!factory) {
|
|
450
|
-
throw new Error(
|
|
451
|
-
`Sink module "${modulePath}" has no export "${factoryName}"`
|
|
452
|
-
);
|
|
453
|
-
}
|
|
454
|
-
return factory(sinkOptions);
|
|
455
|
-
}
|
|
456
|
-
async function configureLogging(loggingConfig, options = {}) {
|
|
457
|
-
const { level, sinks: defaultSinkConfigs, categories } = loggingConfig;
|
|
458
|
-
const reset = options.reset !== false;
|
|
459
|
-
const allSinkConfigs = /* @__PURE__ */ new Map();
|
|
460
|
-
const sinkNameMap = /* @__PURE__ */ new Map();
|
|
461
|
-
for (let i = 0; i < defaultSinkConfigs.length; i++) {
|
|
462
|
-
const config = defaultSinkConfigs[i];
|
|
463
|
-
const name = `sink_${i}`;
|
|
464
|
-
allSinkConfigs.set(name, config);
|
|
465
|
-
sinkNameMap.set(config, name);
|
|
466
|
-
}
|
|
467
|
-
let sinkIndex = defaultSinkConfigs.length;
|
|
468
|
-
for (const [_, categoryConfig] of Object.entries(categories)) {
|
|
469
|
-
if (categoryConfig.sinks) {
|
|
470
|
-
for (const config of categoryConfig.sinks) {
|
|
471
|
-
let found = false;
|
|
472
|
-
for (const [existingConfig, _name] of sinkNameMap) {
|
|
473
|
-
if (JSON.stringify(existingConfig) === JSON.stringify(config)) {
|
|
474
|
-
found = true;
|
|
475
|
-
break;
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
if (!found) {
|
|
479
|
-
const name = `sink_${sinkIndex++}`;
|
|
480
|
-
allSinkConfigs.set(name, config);
|
|
481
|
-
sinkNameMap.set(config, name);
|
|
482
|
-
}
|
|
483
|
-
}
|
|
21
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
22
|
+
validateConfig(value, fullPath);
|
|
484
23
|
}
|
|
485
24
|
}
|
|
486
|
-
const sinks = {};
|
|
487
|
-
for (const [name, config] of allSinkConfigs) {
|
|
488
|
-
sinks[name] = await createSink(config, { cwd: options.cwd });
|
|
489
|
-
}
|
|
490
|
-
const getSinkNames = (configs) => {
|
|
491
|
-
return configs.map((config) => {
|
|
492
|
-
for (const [existingConfig, name] of sinkNameMap) {
|
|
493
|
-
if (JSON.stringify(existingConfig) === JSON.stringify(config)) {
|
|
494
|
-
return name;
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
return "";
|
|
498
|
-
}).filter(Boolean);
|
|
499
|
-
};
|
|
500
|
-
const defaultSinkNames = getSinkNames(defaultSinkConfigs);
|
|
501
|
-
const loggers = SHOVEL_CATEGORIES.map((category) => {
|
|
502
|
-
const categoryConfig = categories[category];
|
|
503
|
-
const categoryLevel = categoryConfig?.level || level;
|
|
504
|
-
const categorySinks = categoryConfig?.sinks ? getSinkNames(categoryConfig.sinks) : defaultSinkNames;
|
|
505
|
-
return {
|
|
506
|
-
category: [category],
|
|
507
|
-
level: categoryLevel,
|
|
508
|
-
sinks: categorySinks
|
|
509
|
-
};
|
|
510
|
-
});
|
|
511
|
-
loggers.push({
|
|
512
|
-
category: ["logtape", "meta"],
|
|
513
|
-
level: "warning",
|
|
514
|
-
sinks: []
|
|
515
|
-
});
|
|
516
|
-
await configure({
|
|
517
|
-
reset,
|
|
518
|
-
sinks,
|
|
519
|
-
loggers
|
|
520
|
-
});
|
|
521
|
-
}
|
|
522
|
-
function getCacheConfig(config, name) {
|
|
523
|
-
return matchPattern(name, config.caches) || {};
|
|
524
|
-
}
|
|
525
|
-
function getBucketConfig(config, name) {
|
|
526
|
-
return matchPattern(name, config.buckets) || {};
|
|
527
|
-
}
|
|
528
|
-
var WELL_KNOWN_BUCKET_PATHS = {
|
|
529
|
-
static: (baseDir) => resolve(baseDir, "../static"),
|
|
530
|
-
server: (baseDir) => baseDir
|
|
531
|
-
};
|
|
532
|
-
var BUILTIN_BUCKET_PROVIDERS = {
|
|
533
|
-
node: "@b9g/filesystem/node.js",
|
|
534
|
-
memory: "@b9g/filesystem/memory.js",
|
|
535
|
-
s3: "@b9g/filesystem-s3"
|
|
536
|
-
};
|
|
537
|
-
function createBucketFactory(options) {
|
|
538
|
-
const { baseDir, config } = options;
|
|
539
|
-
return async (name) => {
|
|
540
|
-
const bucketConfig = config ? getBucketConfig(config, name) : {};
|
|
541
|
-
let bucketPath;
|
|
542
|
-
if (bucketConfig.path) {
|
|
543
|
-
bucketPath = String(bucketConfig.path);
|
|
544
|
-
} else if (WELL_KNOWN_BUCKET_PATHS[name]) {
|
|
545
|
-
bucketPath = WELL_KNOWN_BUCKET_PATHS[name](baseDir);
|
|
546
|
-
} else {
|
|
547
|
-
bucketPath = resolve(baseDir, `../${name}`);
|
|
548
|
-
}
|
|
549
|
-
const provider = String(bucketConfig.provider || "node");
|
|
550
|
-
const modulePath = BUILTIN_BUCKET_PROVIDERS[provider] || provider;
|
|
551
|
-
if (modulePath === "@b9g/filesystem/node.js") {
|
|
552
|
-
const { NodeBucket } = await import("@b9g/filesystem/node.js");
|
|
553
|
-
return new NodeBucket(bucketPath);
|
|
554
|
-
}
|
|
555
|
-
if (modulePath === "@b9g/filesystem/memory.js") {
|
|
556
|
-
const { MemoryBucket } = await import("@b9g/filesystem/memory.js");
|
|
557
|
-
return new MemoryBucket(name);
|
|
558
|
-
}
|
|
559
|
-
try {
|
|
560
|
-
const module = await import(modulePath);
|
|
561
|
-
const BucketClass = module.default || // Default export
|
|
562
|
-
module.S3Bucket || // Named export for s3
|
|
563
|
-
module.Bucket || // Generic Bucket export
|
|
564
|
-
Object.values(module).find(
|
|
565
|
-
(v) => typeof v === "function" && v.name?.includes("Bucket")
|
|
566
|
-
);
|
|
567
|
-
if (!BucketClass) {
|
|
568
|
-
throw new Error(
|
|
569
|
-
`Bucket module "${modulePath}" does not export a valid bucket class. Expected a default export or named export (S3Bucket, Bucket).`
|
|
570
|
-
);
|
|
571
|
-
}
|
|
572
|
-
const { provider: _, path: __, ...bucketOptions } = bucketConfig;
|
|
573
|
-
return new BucketClass(name, { path: bucketPath, ...bucketOptions });
|
|
574
|
-
} catch (error) {
|
|
575
|
-
if (error.code === "ERR_MODULE_NOT_FOUND" || error.code === "MODULE_NOT_FOUND") {
|
|
576
|
-
throw new Error(
|
|
577
|
-
`Bucket provider "${provider}" not found. Make sure the module "${modulePath}" is installed.
|
|
578
|
-
For S3: npm install @b9g/filesystem-s3`
|
|
579
|
-
);
|
|
580
|
-
}
|
|
581
|
-
throw error;
|
|
582
|
-
}
|
|
583
|
-
};
|
|
584
|
-
}
|
|
585
|
-
var BUILTIN_CACHE_PROVIDERS = {
|
|
586
|
-
memory: "@b9g/cache/memory.js",
|
|
587
|
-
redis: "@b9g/cache-redis"
|
|
588
|
-
};
|
|
589
|
-
function createCacheFactory(options = {}) {
|
|
590
|
-
const { config, defaultProvider = "memory" } = options;
|
|
591
|
-
return async (name) => {
|
|
592
|
-
const cacheConfig = config ? getCacheConfig(config, name) : {};
|
|
593
|
-
const provider = String(cacheConfig.provider || defaultProvider);
|
|
594
|
-
if (provider === "cloudflare") {
|
|
595
|
-
const nativeCaches = globalThis.__cloudflareCaches ?? globalThis.caches;
|
|
596
|
-
if (!nativeCaches) {
|
|
597
|
-
throw new Error(
|
|
598
|
-
"Cloudflare cache provider requires native caches API. This provider only works in Cloudflare Workers environment."
|
|
599
|
-
);
|
|
600
|
-
}
|
|
601
|
-
return nativeCaches.open(name);
|
|
602
|
-
}
|
|
603
|
-
const { provider: _, ...cacheOptions } = cacheConfig;
|
|
604
|
-
const modulePath = BUILTIN_CACHE_PROVIDERS[provider] || provider;
|
|
605
|
-
try {
|
|
606
|
-
const module = await import(modulePath);
|
|
607
|
-
const CacheClass = module.default || // Default export
|
|
608
|
-
module.RedisCache || // Named export for redis
|
|
609
|
-
module.MemoryCache || // Named export for memory
|
|
610
|
-
module.Cache || // Generic Cache export
|
|
611
|
-
Object.values(module).find(
|
|
612
|
-
(v) => typeof v === "function" && v.prototype instanceof Cache
|
|
613
|
-
);
|
|
614
|
-
if (!CacheClass) {
|
|
615
|
-
throw new Error(
|
|
616
|
-
`Cache module "${modulePath}" does not export a valid cache class. Expected a default export or named export (RedisCache, Cache) that extends Cache.`
|
|
617
|
-
);
|
|
618
|
-
}
|
|
619
|
-
return new CacheClass(name, cacheOptions);
|
|
620
|
-
} catch (error) {
|
|
621
|
-
if (error.code === "ERR_MODULE_NOT_FOUND" || error.code === "MODULE_NOT_FOUND") {
|
|
622
|
-
throw new Error(
|
|
623
|
-
`Cache provider "${provider}" not found. Make sure the module "${modulePath}" is installed.
|
|
624
|
-
For redis: npm install @b9g/cache-redis`
|
|
625
|
-
);
|
|
626
|
-
}
|
|
627
|
-
throw error;
|
|
628
|
-
}
|
|
629
|
-
};
|
|
630
25
|
}
|
|
631
26
|
export {
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
createCacheFactory,
|
|
635
|
-
getBucketConfig,
|
|
636
|
-
getCacheConfig,
|
|
637
|
-
loadConfig,
|
|
638
|
-
matchPattern,
|
|
639
|
-
parseConfigExpr,
|
|
640
|
-
processConfigValue
|
|
27
|
+
ConfigValidationError,
|
|
28
|
+
validateConfig
|
|
641
29
|
};
|