@usebetterdev/tenant-core 0.1.0 → 0.2.0-beta.12
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 +33 -20
- package/dist/adapter.d.ts +3 -3
- package/dist/adapter.d.ts.map +1 -1
- package/dist/api.d.ts +6 -4
- package/dist/api.d.ts.map +1 -1
- package/dist/better-tenant.d.ts +13 -18
- package/dist/better-tenant.d.ts.map +1 -1
- package/dist/index.cjs +197 -122
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +196 -119
- package/dist/index.js.map +1 -1
- package/dist/request.d.ts.map +1 -1
- package/dist/resolver.d.ts +6 -5
- package/dist/resolver.d.ts.map +1 -1
- package/dist/telemetry.d.ts +6 -4
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/types.d.ts +35 -16
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -24,15 +24,13 @@ __export(index_exports, {
|
|
|
24
24
|
TenantNotResolvedError: () => TenantNotResolvedError,
|
|
25
25
|
betterTenant: () => betterTenant,
|
|
26
26
|
createTenantApi: () => createTenantApi,
|
|
27
|
+
describeStrategies: () => describeStrategies,
|
|
27
28
|
getContext: () => getContext,
|
|
28
|
-
getDatabase: () => getDatabase,
|
|
29
29
|
handleRequest: () => handleRequest,
|
|
30
30
|
resolveTenant: () => resolveTenant,
|
|
31
|
-
resolveTenantAsync: () => resolveTenantAsync,
|
|
32
31
|
runAs: () => runAs,
|
|
33
32
|
runAsSystem: () => runAsSystem,
|
|
34
33
|
runWithTenant: () => runWithTenant,
|
|
35
|
-
runWithTenantAndDatabase: () => runWithTenantAndDatabase,
|
|
36
34
|
sendCliTelemetry: () => sendCliTelemetry,
|
|
37
35
|
toResolvableRequest: () => toResolvableRequest
|
|
38
36
|
});
|
|
@@ -55,8 +53,7 @@ function getTelemetryTenantConfig(config) {
|
|
|
55
53
|
jwt: !!r.jwt,
|
|
56
54
|
custom: !!r.custom
|
|
57
55
|
},
|
|
58
|
-
tenantTablesCount:
|
|
59
|
-
hasGetTenantRepository: !!config.getTenantRepository,
|
|
56
|
+
tenantTablesCount: 0,
|
|
60
57
|
loadTenant: config.loadTenant,
|
|
61
58
|
basePathSet: !!config.basePath,
|
|
62
59
|
plugins: (config.plugins ?? []).map((p) => String(p.id))
|
|
@@ -72,7 +69,9 @@ function getTelemetryCliConfig(config) {
|
|
|
72
69
|
function getAnonymousId(cwd) {
|
|
73
70
|
try {
|
|
74
71
|
const pkgPath = (0, import_node_path.join)(cwd, "package.json");
|
|
75
|
-
if (!(0, import_node_fs.existsSync)(pkgPath))
|
|
72
|
+
if (!(0, import_node_fs.existsSync)(pkgPath)) {
|
|
73
|
+
return void 0;
|
|
74
|
+
}
|
|
76
75
|
const pkg = JSON.parse((0, import_node_fs.readFileSync)(pkgPath, "utf-8"));
|
|
77
76
|
const name = typeof pkg?.name === "string" ? pkg.name : "";
|
|
78
77
|
const basePath = typeof pkg?.betterTenant?.basePath === "string" ? pkg.betterTenant.basePath : "";
|
|
@@ -101,22 +100,34 @@ function detectRuntime() {
|
|
|
101
100
|
}
|
|
102
101
|
function detectEnvironment() {
|
|
103
102
|
const env = process.env.NODE_ENV || "development";
|
|
104
|
-
if (env === "test")
|
|
103
|
+
if (env === "test") {
|
|
104
|
+
return "test";
|
|
105
|
+
}
|
|
105
106
|
if (process.env.CI === "true" || process.env.GITHUB_ACTIONS === "true" || process.env.GITLAB_CI === "true" || process.env.CIRCLECI === "true") {
|
|
106
107
|
return "ci";
|
|
107
108
|
}
|
|
108
|
-
if (env === "production")
|
|
109
|
+
if (env === "production") {
|
|
110
|
+
return "production";
|
|
111
|
+
}
|
|
109
112
|
return "development";
|
|
110
113
|
}
|
|
111
114
|
function detectFramework(cwd) {
|
|
112
115
|
try {
|
|
113
116
|
const pkgPath = (0, import_node_path.join)(cwd, "package.json");
|
|
114
|
-
if (!(0, import_node_fs.existsSync)(pkgPath))
|
|
117
|
+
if (!(0, import_node_fs.existsSync)(pkgPath)) {
|
|
118
|
+
return void 0;
|
|
119
|
+
}
|
|
115
120
|
const pkg = JSON.parse((0, import_node_fs.readFileSync)(pkgPath, "utf-8"));
|
|
116
121
|
const deps = { ...pkg?.dependencies, ...pkg?.devDependencies };
|
|
117
|
-
if (deps["next"])
|
|
118
|
-
|
|
119
|
-
|
|
122
|
+
if (deps["next"]) {
|
|
123
|
+
return { name: "next", version: deps["next"] };
|
|
124
|
+
}
|
|
125
|
+
if (deps["hono"]) {
|
|
126
|
+
return { name: "hono", version: deps["hono"] };
|
|
127
|
+
}
|
|
128
|
+
if (deps["express"]) {
|
|
129
|
+
return { name: "express", version: deps["express"] };
|
|
130
|
+
}
|
|
120
131
|
return void 0;
|
|
121
132
|
} catch {
|
|
122
133
|
return void 0;
|
|
@@ -125,12 +136,17 @@ function detectFramework(cwd) {
|
|
|
125
136
|
function detectDatabase(cwd) {
|
|
126
137
|
try {
|
|
127
138
|
const pkgPath = (0, import_node_path.join)(cwd, "package.json");
|
|
128
|
-
if (!(0, import_node_fs.existsSync)(pkgPath))
|
|
139
|
+
if (!(0, import_node_fs.existsSync)(pkgPath)) {
|
|
140
|
+
return void 0;
|
|
141
|
+
}
|
|
129
142
|
const pkg = JSON.parse((0, import_node_fs.readFileSync)(pkgPath, "utf-8"));
|
|
130
143
|
const deps = { ...pkg?.dependencies, ...pkg?.devDependencies };
|
|
131
|
-
if (deps["drizzle-orm"])
|
|
144
|
+
if (deps["drizzle-orm"]) {
|
|
132
145
|
return { name: "drizzle", version: deps["drizzle-orm"] };
|
|
133
|
-
|
|
146
|
+
}
|
|
147
|
+
if (deps["prisma"]) {
|
|
148
|
+
return { name: "prisma", version: deps["prisma"] };
|
|
149
|
+
}
|
|
134
150
|
return void 0;
|
|
135
151
|
} catch {
|
|
136
152
|
return void 0;
|
|
@@ -151,31 +167,46 @@ function detectSystem() {
|
|
|
151
167
|
}
|
|
152
168
|
function detectPackageManager() {
|
|
153
169
|
const ua = process.env.npm_config_user_agent;
|
|
154
|
-
if (!ua || typeof ua !== "string")
|
|
170
|
+
if (!ua || typeof ua !== "string") {
|
|
171
|
+
return void 0;
|
|
172
|
+
}
|
|
155
173
|
const match = ua.match(/^(.+?)\/(\d+\.\d+\.\d+.*?)(?:\s|$)/);
|
|
156
174
|
if (match) {
|
|
157
175
|
const name = (match[1] ?? "unknown").toLowerCase();
|
|
158
176
|
const version = match[2];
|
|
159
177
|
return version ? { name, version } : { name };
|
|
160
178
|
}
|
|
161
|
-
if (ua.includes("pnpm"))
|
|
162
|
-
|
|
179
|
+
if (ua.includes("pnpm")) {
|
|
180
|
+
return { name: "pnpm" };
|
|
181
|
+
}
|
|
182
|
+
if (ua.includes("yarn")) {
|
|
183
|
+
return { name: "yarn" };
|
|
184
|
+
}
|
|
163
185
|
return { name: "npm" };
|
|
164
186
|
}
|
|
165
187
|
function isTelemetryEnabled(options) {
|
|
166
|
-
if (process.env.NODE_ENV === "test")
|
|
188
|
+
if (process.env.NODE_ENV === "test") {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
167
191
|
const env = process.env.BETTER_TENANT_TELEMETRY;
|
|
168
|
-
if (env === "0" || env?.toLowerCase() === "false")
|
|
169
|
-
|
|
192
|
+
if (env === "0" || env?.toLowerCase() === "false") {
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
if (env === "1" || env?.toLowerCase() === "true") {
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
170
198
|
return options?.enabled !== false;
|
|
171
199
|
}
|
|
172
200
|
function isDebugMode(options) {
|
|
173
|
-
if (process.env.BETTER_TENANT_TELEMETRY_DEBUG === "1")
|
|
201
|
+
if (process.env.BETTER_TENANT_TELEMETRY_DEBUG === "1") {
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
174
204
|
return options?.debug === true;
|
|
175
205
|
}
|
|
176
206
|
function sendTelemetry(type, payload, options) {
|
|
177
|
-
if (!isTelemetryEnabled(options))
|
|
207
|
+
if (!isTelemetryEnabled(options)) {
|
|
178
208
|
return options?.wait ? Promise.resolve() : void 0;
|
|
209
|
+
}
|
|
179
210
|
const fullPayload = {
|
|
180
211
|
library: LIBRARY_ID,
|
|
181
212
|
type,
|
|
@@ -273,9 +304,10 @@ function getDatabase() {
|
|
|
273
304
|
async function runWithTenantAndDatabase(tenantId, adapter, fn, options) {
|
|
274
305
|
const result = await adapter.runWithTenant(tenantId, async (database) => {
|
|
275
306
|
let context = { tenantId, database };
|
|
276
|
-
|
|
307
|
+
const getTenantRepository = options?.getTenantRepository;
|
|
308
|
+
if (options?.loadTenant && getTenantRepository && adapter.runAsSystem) {
|
|
277
309
|
const tenant = await adapter.runAsSystem(
|
|
278
|
-
(systemDb) =>
|
|
310
|
+
(systemDb) => getTenantRepository(systemDb).getById(tenantId)
|
|
279
311
|
);
|
|
280
312
|
context = tenant != null ? { ...context, tenant } : context;
|
|
281
313
|
}
|
|
@@ -340,17 +372,7 @@ function requireRunAsSystem(adapter) {
|
|
|
340
372
|
}
|
|
341
373
|
return adapter.runAsSystem;
|
|
342
374
|
}
|
|
343
|
-
function requireTenantRepository(getTenantRepository) {
|
|
344
|
-
if (!getTenantRepository) {
|
|
345
|
-
throw new Error(
|
|
346
|
-
"better-tenant: tenant.api requires getTenantRepository in config (adapter provides CRUD for tenants table)"
|
|
347
|
-
);
|
|
348
|
-
}
|
|
349
|
-
return getTenantRepository;
|
|
350
|
-
}
|
|
351
375
|
function createTenantApi(adapter, getTenantRepository) {
|
|
352
|
-
const runAsSystem2 = requireRunAsSystem(adapter);
|
|
353
|
-
const getRepository = requireTenantRepository(getTenantRepository);
|
|
354
376
|
return {
|
|
355
377
|
async createTenant(data) {
|
|
356
378
|
if (!data.name?.trim()) {
|
|
@@ -359,8 +381,9 @@ function createTenantApi(adapter, getTenantRepository) {
|
|
|
359
381
|
if (!data.slug?.trim()) {
|
|
360
382
|
throw new Error("better-tenant: createTenant requires slug");
|
|
361
383
|
}
|
|
384
|
+
const runAsSystem2 = requireRunAsSystem(adapter);
|
|
362
385
|
return runAsSystem2(
|
|
363
|
-
(database) =>
|
|
386
|
+
(database) => getTenantRepository(database).create({
|
|
364
387
|
name: data.name.trim(),
|
|
365
388
|
slug: data.slug.trim()
|
|
366
389
|
})
|
|
@@ -370,8 +393,9 @@ function createTenantApi(adapter, getTenantRepository) {
|
|
|
370
393
|
if (!tenantId?.trim()) {
|
|
371
394
|
throw new Error("better-tenant: updateTenant requires tenantId");
|
|
372
395
|
}
|
|
396
|
+
const runAsSystem2 = requireRunAsSystem(adapter);
|
|
373
397
|
return runAsSystem2(
|
|
374
|
-
(database) =>
|
|
398
|
+
(database) => getTenantRepository(database).update(tenantId, {
|
|
375
399
|
...data.name !== void 0 && { name: data.name },
|
|
376
400
|
...data.slug !== void 0 && { slug: data.slug }
|
|
377
401
|
})
|
|
@@ -383,16 +407,18 @@ function createTenantApi(adapter, getTenantRepository) {
|
|
|
383
407
|
MAX_LIST_LIMIT
|
|
384
408
|
);
|
|
385
409
|
const offset = Math.max(0, options.offset ?? 0);
|
|
410
|
+
const runAsSystem2 = requireRunAsSystem(adapter);
|
|
386
411
|
return runAsSystem2(
|
|
387
|
-
(database) =>
|
|
412
|
+
(database) => getTenantRepository(database).list({ limit, offset })
|
|
388
413
|
);
|
|
389
414
|
},
|
|
390
415
|
async deleteTenant(tenantId) {
|
|
391
416
|
if (!tenantId?.trim()) {
|
|
392
417
|
throw new Error("better-tenant: deleteTenant requires tenantId");
|
|
393
418
|
}
|
|
419
|
+
const runAsSystem2 = requireRunAsSystem(adapter);
|
|
394
420
|
return runAsSystem2(
|
|
395
|
-
(database) =>
|
|
421
|
+
(database) => getTenantRepository(database).delete(tenantId)
|
|
396
422
|
);
|
|
397
423
|
}
|
|
398
424
|
};
|
|
@@ -402,10 +428,7 @@ async function runAs(tenantId, adapter, fn) {
|
|
|
402
428
|
}
|
|
403
429
|
async function runAsSystem(adapter, fn) {
|
|
404
430
|
const run = requireRunAsSystem(adapter);
|
|
405
|
-
return runWithContext(
|
|
406
|
-
{ tenantId: "", isSystem: true },
|
|
407
|
-
() => run(fn)
|
|
408
|
-
);
|
|
431
|
+
return runWithContext({ isSystem: true }, () => run(fn));
|
|
409
432
|
}
|
|
410
433
|
|
|
411
434
|
// src/resolver.ts
|
|
@@ -416,17 +439,23 @@ function getHeader(headers, name) {
|
|
|
416
439
|
return value2?.trim() || void 0;
|
|
417
440
|
}
|
|
418
441
|
const raw = headers[key] ?? headers[name];
|
|
419
|
-
if (raw === void 0)
|
|
442
|
+
if (raw === void 0) {
|
|
443
|
+
return void 0;
|
|
444
|
+
}
|
|
420
445
|
const value = Array.isArray(raw) ? raw[0] : raw;
|
|
421
446
|
return (typeof value === "string" ? value : "").trim() || void 0;
|
|
422
447
|
}
|
|
423
448
|
function resolveFromHeader(request, headerName) {
|
|
424
|
-
if (!headerName)
|
|
449
|
+
if (!headerName) {
|
|
450
|
+
return void 0;
|
|
451
|
+
}
|
|
425
452
|
return getHeader(request.headers, headerName);
|
|
426
453
|
}
|
|
427
454
|
function resolveFromPath(request, pathConfig) {
|
|
428
455
|
const pathOrUrl = request.path ?? request.url;
|
|
429
|
-
if (!pathOrUrl)
|
|
456
|
+
if (!pathOrUrl) {
|
|
457
|
+
return void 0;
|
|
458
|
+
}
|
|
430
459
|
let path;
|
|
431
460
|
try {
|
|
432
461
|
path = pathOrUrl.startsWith("http") ? new URL(pathOrUrl).pathname : pathOrUrl;
|
|
@@ -446,11 +475,17 @@ function parseTenantSegmentIndex(pattern) {
|
|
|
446
475
|
}
|
|
447
476
|
function resolveFromSubdomain(request, config) {
|
|
448
477
|
const host = request.host ?? (request.url ? new URL(request.url).host : "");
|
|
449
|
-
if (!host)
|
|
478
|
+
if (!host) {
|
|
479
|
+
return void 0;
|
|
480
|
+
}
|
|
450
481
|
const hostname = host.split(":")[0];
|
|
451
|
-
if (!hostname)
|
|
482
|
+
if (!hostname) {
|
|
483
|
+
return void 0;
|
|
484
|
+
}
|
|
452
485
|
const parts = hostname.split(".");
|
|
453
|
-
if (parts.length <= 2)
|
|
486
|
+
if (parts.length <= 2) {
|
|
487
|
+
return void 0;
|
|
488
|
+
}
|
|
454
489
|
const index = config === true ? 0 : typeof config === "object" && config.segmentIndex !== void 0 ? config.segmentIndex : 0;
|
|
455
490
|
const value = parts[index];
|
|
456
491
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
@@ -458,125 +493,159 @@ function resolveFromSubdomain(request, config) {
|
|
|
458
493
|
function decodeJwtPayload(token) {
|
|
459
494
|
try {
|
|
460
495
|
const parts = token.split(".");
|
|
461
|
-
if (parts.length < 2)
|
|
496
|
+
if (parts.length < 2) {
|
|
497
|
+
return null;
|
|
498
|
+
}
|
|
462
499
|
const payload = parts[1];
|
|
463
|
-
if (!payload)
|
|
500
|
+
if (!payload) {
|
|
501
|
+
return null;
|
|
502
|
+
}
|
|
464
503
|
const decoded = atob(payload.replace(/-/g, "+").replace(/_/g, "/"));
|
|
465
|
-
|
|
504
|
+
const parsed = JSON.parse(decoded);
|
|
505
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
506
|
+
return null;
|
|
507
|
+
}
|
|
508
|
+
return parsed;
|
|
466
509
|
} catch {
|
|
467
510
|
return null;
|
|
468
511
|
}
|
|
469
512
|
}
|
|
470
|
-
function resolveFromJwt(request, config) {
|
|
471
|
-
const getToken = request.getToken;
|
|
472
|
-
if (!getToken) return void 0;
|
|
473
|
-
const token = typeof getToken === "function" ? getToken() : getToken;
|
|
474
|
-
const value = token instanceof Promise ? void 0 : token ?? void 0;
|
|
475
|
-
const resolved = value ?? void 0;
|
|
476
|
-
if (!resolved) return void 0;
|
|
477
|
-
const claim = typeof config === "string" ? config : config.claim;
|
|
478
|
-
if (!claim) return void 0;
|
|
479
|
-
const payload = decodeJwtPayload(resolved);
|
|
480
|
-
if (!payload) return void 0;
|
|
481
|
-
const claimValue = payload[claim];
|
|
482
|
-
return typeof claimValue === "string" && claimValue.length > 0 ? claimValue : void 0;
|
|
483
|
-
}
|
|
484
|
-
async function resolveFromJwtAsync(request, config) {
|
|
513
|
+
async function resolveFromJwt(request, config) {
|
|
485
514
|
const getToken = request.getToken;
|
|
486
|
-
if (!getToken)
|
|
515
|
+
if (!getToken) {
|
|
516
|
+
return void 0;
|
|
517
|
+
}
|
|
487
518
|
const token = typeof getToken === "function" ? getToken() : getToken;
|
|
488
519
|
const value = token instanceof Promise ? await token : token;
|
|
489
520
|
const resolved = value ?? void 0;
|
|
490
|
-
if (!resolved)
|
|
521
|
+
if (!resolved) {
|
|
522
|
+
return void 0;
|
|
523
|
+
}
|
|
491
524
|
const claim = typeof config === "string" ? config : config.claim;
|
|
492
|
-
if (!claim)
|
|
493
|
-
|
|
494
|
-
|
|
525
|
+
if (!claim) {
|
|
526
|
+
return void 0;
|
|
527
|
+
}
|
|
528
|
+
const verifyToken = typeof config === "object" ? config.verifyToken : void 0;
|
|
529
|
+
let payload;
|
|
530
|
+
if (verifyToken) {
|
|
531
|
+
payload = await verifyToken(resolved);
|
|
532
|
+
} else {
|
|
533
|
+
payload = decodeJwtPayload(resolved);
|
|
534
|
+
}
|
|
535
|
+
if (!payload) {
|
|
536
|
+
return void 0;
|
|
537
|
+
}
|
|
495
538
|
const claimValue = payload[claim];
|
|
496
539
|
return typeof claimValue === "string" && claimValue.length > 0 ? claimValue : void 0;
|
|
497
540
|
}
|
|
498
|
-
function
|
|
541
|
+
function describeStrategies(config) {
|
|
542
|
+
const strategies = [];
|
|
499
543
|
if (config.header !== void 0) {
|
|
500
|
-
|
|
501
|
-
if (v !== void 0) return v;
|
|
544
|
+
strategies.push(`header '${config.header}'`);
|
|
502
545
|
}
|
|
503
546
|
if (config.path !== void 0) {
|
|
504
|
-
const
|
|
505
|
-
|
|
547
|
+
const pattern = typeof config.path === "string" ? config.path : config.path.pattern;
|
|
548
|
+
strategies.push(`path '${pattern}'`);
|
|
506
549
|
}
|
|
507
550
|
if (config.subdomain !== void 0 && config.subdomain !== false) {
|
|
508
|
-
|
|
509
|
-
if (v !== void 0) return v;
|
|
551
|
+
strategies.push("subdomain");
|
|
510
552
|
}
|
|
511
553
|
if (config.jwt !== void 0) {
|
|
512
|
-
const
|
|
513
|
-
|
|
554
|
+
const claim = typeof config.jwt === "string" ? config.jwt : config.jwt.claim;
|
|
555
|
+
strategies.push(`jwt claim '${claim}'`);
|
|
514
556
|
}
|
|
515
557
|
if (config.custom !== void 0) {
|
|
516
|
-
|
|
517
|
-
if (typeof v === "string" && v.length > 0) return v;
|
|
558
|
+
strategies.push("custom resolver");
|
|
518
559
|
}
|
|
519
|
-
return
|
|
560
|
+
return strategies;
|
|
520
561
|
}
|
|
521
|
-
async function
|
|
562
|
+
async function resolveTenant(request, config) {
|
|
522
563
|
if (config.header !== void 0) {
|
|
523
564
|
const v = resolveFromHeader(request, config.header);
|
|
524
|
-
if (v !== void 0)
|
|
565
|
+
if (v !== void 0) {
|
|
566
|
+
return v;
|
|
567
|
+
}
|
|
525
568
|
}
|
|
526
569
|
if (config.path !== void 0) {
|
|
527
570
|
const v = resolveFromPath(request, config.path);
|
|
528
|
-
if (v !== void 0)
|
|
571
|
+
if (v !== void 0) {
|
|
572
|
+
return v;
|
|
573
|
+
}
|
|
529
574
|
}
|
|
530
575
|
if (config.subdomain !== void 0 && config.subdomain !== false) {
|
|
531
576
|
const v = resolveFromSubdomain(request, config.subdomain);
|
|
532
|
-
if (v !== void 0)
|
|
577
|
+
if (v !== void 0) {
|
|
578
|
+
return v;
|
|
579
|
+
}
|
|
533
580
|
}
|
|
534
581
|
if (config.jwt !== void 0) {
|
|
535
|
-
const v = await
|
|
536
|
-
if (v !== void 0)
|
|
582
|
+
const v = await resolveFromJwt(request, config.jwt);
|
|
583
|
+
if (v !== void 0) {
|
|
584
|
+
return v;
|
|
585
|
+
}
|
|
537
586
|
}
|
|
538
587
|
if (config.custom !== void 0) {
|
|
539
588
|
const v = await Promise.resolve(config.custom(request));
|
|
540
|
-
if (typeof v === "string" && v.length > 0)
|
|
589
|
+
if (typeof v === "string" && v.length > 0) {
|
|
590
|
+
return v;
|
|
591
|
+
}
|
|
541
592
|
}
|
|
542
593
|
return void 0;
|
|
543
594
|
}
|
|
544
595
|
|
|
545
596
|
// src/better-tenant.ts
|
|
597
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
598
|
+
async function resolveIdentifierToId(identifier, resolverConfig, adapter, getTenantRepository) {
|
|
599
|
+
if (resolverConfig.resolveToId) {
|
|
600
|
+
return resolverConfig.resolveToId(identifier);
|
|
601
|
+
}
|
|
602
|
+
if (UUID_RE.test(identifier)) {
|
|
603
|
+
return identifier;
|
|
604
|
+
}
|
|
605
|
+
if (adapter.runAsSystem) {
|
|
606
|
+
const tenant = await adapter.runAsSystem(
|
|
607
|
+
(systemDb) => getTenantRepository(systemDb).getBySlug(identifier)
|
|
608
|
+
);
|
|
609
|
+
return tenant?.id;
|
|
610
|
+
}
|
|
611
|
+
return identifier;
|
|
612
|
+
}
|
|
546
613
|
function betterTenant(config) {
|
|
547
|
-
const {
|
|
614
|
+
const { database, tenantResolver, loadTenant } = config;
|
|
615
|
+
const { adapter, getTenantRepository } = database;
|
|
548
616
|
sendInitTelemetry(config, config.telemetry);
|
|
549
|
-
const api =
|
|
550
|
-
const
|
|
551
|
-
const
|
|
617
|
+
const api = createTenantApi(adapter, getTenantRepository);
|
|
618
|
+
const runWithTenantAndDatabaseOptions = loadTenant !== false ? { loadTenant: true, getTenantRepository } : void 0;
|
|
619
|
+
const resolverStrategies = describeStrategies(tenantResolver);
|
|
620
|
+
async function resolveAndNormalize(request) {
|
|
621
|
+
const raw = await resolveTenant(request, tenantResolver);
|
|
622
|
+
if (!raw) return void 0;
|
|
623
|
+
return resolveIdentifierToId(
|
|
624
|
+
raw,
|
|
625
|
+
tenantResolver,
|
|
626
|
+
adapter,
|
|
627
|
+
getTenantRepository
|
|
628
|
+
);
|
|
629
|
+
}
|
|
552
630
|
return {
|
|
553
631
|
getContext,
|
|
554
|
-
getDatabase,
|
|
632
|
+
getDatabase: () => getDatabase(),
|
|
555
633
|
runWithTenant,
|
|
556
|
-
|
|
557
|
-
|
|
634
|
+
runAs: (tenantId, fn) => runWithTenantAndDatabase(
|
|
635
|
+
tenantId,
|
|
636
|
+
adapter,
|
|
637
|
+
fn,
|
|
638
|
+
runWithTenantAndDatabaseOptions
|
|
639
|
+
),
|
|
558
640
|
runAsSystem: (fn) => runAsSystem(adapter, fn),
|
|
559
|
-
resolveTenant:
|
|
560
|
-
|
|
641
|
+
resolveTenant: resolveAndNormalize,
|
|
642
|
+
resolverStrategies,
|
|
561
643
|
handleRequest: (request, next, options) => handleRequest(request, next, {
|
|
562
644
|
...options,
|
|
563
|
-
resolveTenant:
|
|
645
|
+
resolveTenant: resolveAndNormalize,
|
|
564
646
|
adapter
|
|
565
647
|
}),
|
|
566
|
-
|
|
567
|
-
};
|
|
568
|
-
}
|
|
569
|
-
function createStubTenantApi() {
|
|
570
|
-
const err = () => {
|
|
571
|
-
throw new Error(
|
|
572
|
-
"better-tenant: tenant.api requires getTenantRepository in config"
|
|
573
|
-
);
|
|
574
|
-
};
|
|
575
|
-
return {
|
|
576
|
-
createTenant: () => err(),
|
|
577
|
-
updateTenant: () => err(),
|
|
578
|
-
listTenants: () => err(),
|
|
579
|
-
deleteTenant: () => err()
|
|
648
|
+
api
|
|
580
649
|
};
|
|
581
650
|
}
|
|
582
651
|
|
|
@@ -585,12 +654,16 @@ function isFetchRequest(input) {
|
|
|
585
654
|
return typeof Request !== "undefined" && input instanceof Request;
|
|
586
655
|
}
|
|
587
656
|
function normalizeHost(raw) {
|
|
588
|
-
if (!raw)
|
|
657
|
+
if (!raw) {
|
|
658
|
+
return void 0;
|
|
659
|
+
}
|
|
589
660
|
const value = raw.split(":")[0]?.trim();
|
|
590
661
|
return value || void 0;
|
|
591
662
|
}
|
|
592
663
|
function normalizeHeaders(headers) {
|
|
593
|
-
if (!headers)
|
|
664
|
+
if (!headers) {
|
|
665
|
+
return {};
|
|
666
|
+
}
|
|
594
667
|
const normalized = {};
|
|
595
668
|
for (const [key, value] of Object.entries(headers)) {
|
|
596
669
|
const normalizedKey = key.toLowerCase();
|
|
@@ -607,7 +680,9 @@ function normalizeHeaders(headers) {
|
|
|
607
680
|
return normalized;
|
|
608
681
|
}
|
|
609
682
|
function pathWithoutQuery(value) {
|
|
610
|
-
if (!value)
|
|
683
|
+
if (!value) {
|
|
684
|
+
return void 0;
|
|
685
|
+
}
|
|
611
686
|
const [pathname] = value.split("?");
|
|
612
687
|
return pathname || void 0;
|
|
613
688
|
}
|
|
@@ -641,7 +716,9 @@ function toResolvableRequest(request) {
|
|
|
641
716
|
const hostFromHeaders = headers.host;
|
|
642
717
|
const hostValue = Array.isArray(hostFromHeaders) ? hostFromHeaders[0] : hostFromHeaders;
|
|
643
718
|
const host = normalizeHost(request.hostname ?? request.host ?? hostValue);
|
|
644
|
-
const path = pathWithoutQuery(
|
|
719
|
+
const path = pathWithoutQuery(
|
|
720
|
+
request.path ?? request.originalUrl ?? request.url
|
|
721
|
+
);
|
|
645
722
|
const resolved = {
|
|
646
723
|
headers
|
|
647
724
|
};
|
|
@@ -662,15 +739,13 @@ function toResolvableRequest(request) {
|
|
|
662
739
|
TenantNotResolvedError,
|
|
663
740
|
betterTenant,
|
|
664
741
|
createTenantApi,
|
|
742
|
+
describeStrategies,
|
|
665
743
|
getContext,
|
|
666
|
-
getDatabase,
|
|
667
744
|
handleRequest,
|
|
668
745
|
resolveTenant,
|
|
669
|
-
resolveTenantAsync,
|
|
670
746
|
runAs,
|
|
671
747
|
runAsSystem,
|
|
672
748
|
runWithTenant,
|
|
673
|
-
runWithTenantAndDatabase,
|
|
674
749
|
sendCliTelemetry,
|
|
675
750
|
toResolvableRequest
|
|
676
751
|
});
|