tychat-contracts 1.6.58 → 1.6.59
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/dist/tenants/anamnesis-public-frontend-url.d.ts +10 -0
- package/dist/tenants/anamnesis-public-frontend-url.d.ts.map +1 -0
- package/dist/tenants/anamnesis-public-frontend-url.js +56 -0
- package/dist/tenants/anamnesis-public-frontend-url.spec.d.ts +2 -0
- package/dist/tenants/anamnesis-public-frontend-url.spec.d.ts.map +1 -0
- package/dist/tenants/anamnesis-public-frontend-url.spec.js +23 -0
- package/dist/tenants/index.d.ts +1 -0
- package/dist/tenants/index.d.ts.map +1 -1
- package/dist/tenants/index.js +1 -0
- package/package.json +1 -1
- package/src/tenants/anamnesis-public-frontend-url.spec.ts +45 -0
- package/src/tenants/anamnesis-public-frontend-url.ts +61 -0
- package/src/tenants/index.ts +1 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** True when the hostname's first label matches the tenant slug segment. */
|
|
2
|
+
export declare function hostnameMatchesTenantSlug(hostname: string, tenantSlug: string): boolean;
|
|
3
|
+
/**
|
|
4
|
+
* Builds a frontend base URL that resolves to a single tenant on public routes.
|
|
5
|
+
*
|
|
6
|
+
* Shared hosts (e.g. `qa.tychat.app`) are scoped as `{slug}.qa.tychat.app`.
|
|
7
|
+
* Custom domains and tenant-specific `*.tychat.app` hosts are kept as configured.
|
|
8
|
+
*/
|
|
9
|
+
export declare function resolveTenantScopedFrontendBaseUrl(tenantSlug: string, configuredHostRaw: string | null | undefined): string;
|
|
10
|
+
//# sourceMappingURL=anamnesis-public-frontend-url.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anamnesis-public-frontend-url.d.ts","sourceRoot":"","sources":["../../src/tenants/anamnesis-public-frontend-url.ts"],"names":[],"mappings":"AAiBA,4EAA4E;AAC5E,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAIvF;AAED;;;;;GAKG;AACH,wBAAgB,kCAAkC,CAChD,UAAU,EAAE,MAAM,EAClB,iBAAiB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC3C,MAAM,CA2BR"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.hostnameMatchesTenantSlug = hostnameMatchesTenantSlug;
|
|
4
|
+
exports.resolveTenantScopedFrontendBaseUrl = resolveTenantScopedFrontendBaseUrl;
|
|
5
|
+
const tenant_slug_util_1 = require("./tenant-slug.util");
|
|
6
|
+
const TYCHAT_APP_SUFFIX = '.tychat.app';
|
|
7
|
+
function normalizeFrontendBaseUrl(raw) {
|
|
8
|
+
const trimmed = raw.trim().replace(/\/+$/, '');
|
|
9
|
+
if (!trimmed)
|
|
10
|
+
return '';
|
|
11
|
+
if (/^https?:\/\//i.test(trimmed)) {
|
|
12
|
+
return trimmed;
|
|
13
|
+
}
|
|
14
|
+
return `https://${trimmed}`;
|
|
15
|
+
}
|
|
16
|
+
function firstHostnameLabel(hostname) {
|
|
17
|
+
return hostname.toLowerCase().split('.').filter(Boolean)[0] ?? '';
|
|
18
|
+
}
|
|
19
|
+
/** True when the hostname's first label matches the tenant slug segment. */
|
|
20
|
+
function hostnameMatchesTenantSlug(hostname, tenantSlug) {
|
|
21
|
+
const segment = (0, tenant_slug_util_1.slugToDomainSegment)(tenantSlug);
|
|
22
|
+
if (!segment || segment === 'default')
|
|
23
|
+
return false;
|
|
24
|
+
return firstHostnameLabel(hostname) === segment;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Builds a frontend base URL that resolves to a single tenant on public routes.
|
|
28
|
+
*
|
|
29
|
+
* Shared hosts (e.g. `qa.tychat.app`) are scoped as `{slug}.qa.tychat.app`.
|
|
30
|
+
* Custom domains and tenant-specific `*.tychat.app` hosts are kept as configured.
|
|
31
|
+
*/
|
|
32
|
+
function resolveTenantScopedFrontendBaseUrl(tenantSlug, configuredHostRaw) {
|
|
33
|
+
const slug = tenantSlug?.trim();
|
|
34
|
+
if (!slug)
|
|
35
|
+
return '';
|
|
36
|
+
const segment = (0, tenant_slug_util_1.slugToDomainSegment)(slug);
|
|
37
|
+
const fallback = `${segment}${TYCHAT_APP_SUFFIX}`;
|
|
38
|
+
const configured = configuredHostRaw?.trim();
|
|
39
|
+
const base = normalizeFrontendBaseUrl(configured || fallback);
|
|
40
|
+
let url;
|
|
41
|
+
try {
|
|
42
|
+
url = new URL(base);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return normalizeFrontendBaseUrl(fallback);
|
|
46
|
+
}
|
|
47
|
+
const hostname = url.hostname.toLowerCase();
|
|
48
|
+
if (hostnameMatchesTenantSlug(hostname, slug)) {
|
|
49
|
+
return url.origin;
|
|
50
|
+
}
|
|
51
|
+
if (hostname.endsWith(TYCHAT_APP_SUFFIX)) {
|
|
52
|
+
url.hostname = `${segment}.${hostname}`;
|
|
53
|
+
return url.origin;
|
|
54
|
+
}
|
|
55
|
+
return url.origin;
|
|
56
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anamnesis-public-frontend-url.spec.d.ts","sourceRoot":"","sources":["../../src/tenants/anamnesis-public-frontend-url.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const anamnesis_public_frontend_url_1 = require("./anamnesis-public-frontend-url");
|
|
4
|
+
describe('resolveTenantScopedFrontendBaseUrl', () => {
|
|
5
|
+
it('keeps tenant-specific tychat.app host', () => {
|
|
6
|
+
expect((0, anamnesis_public_frontend_url_1.resolveTenantScopedFrontendBaseUrl)('clinica_sampaio', 'https://clinica-sampaio.tychat.app')).toBe('https://clinica-sampaio.tychat.app');
|
|
7
|
+
});
|
|
8
|
+
it('scopes shared QA host with tenant subdomain', () => {
|
|
9
|
+
expect((0, anamnesis_public_frontend_url_1.resolveTenantScopedFrontendBaseUrl)('clinica_sampaio', 'https://qa.tychat.app')).toBe('https://clinica-sampaio.qa.tychat.app');
|
|
10
|
+
});
|
|
11
|
+
it('keeps custom domain unchanged', () => {
|
|
12
|
+
expect((0, anamnesis_public_frontend_url_1.resolveTenantScopedFrontendBaseUrl)('clinica_sampaio', 'https://app.minhaclinica.com.br')).toBe('https://app.minhaclinica.com.br');
|
|
13
|
+
});
|
|
14
|
+
it('uses slug fallback when host is empty', () => {
|
|
15
|
+
expect((0, anamnesis_public_frontend_url_1.resolveTenantScopedFrontendBaseUrl)('homolog', '')).toBe('https://homolog.tychat.app');
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
describe('hostnameMatchesTenantSlug', () => {
|
|
19
|
+
it('matches first DNS label to slug segment', () => {
|
|
20
|
+
expect((0, anamnesis_public_frontend_url_1.hostnameMatchesTenantSlug)('clinica-sampaio.qa.tychat.app', 'clinica_sampaio')).toBe(true);
|
|
21
|
+
expect((0, anamnesis_public_frontend_url_1.hostnameMatchesTenantSlug)('qa.tychat.app', 'clinica_sampaio')).toBe(false);
|
|
22
|
+
});
|
|
23
|
+
});
|
package/dist/tenants/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export * from './get-tenant-by-address.query.dto';
|
|
|
3
3
|
export * from './tenant-public-by-address-response.dto';
|
|
4
4
|
export * from './whatsapp-provider-kind.dto';
|
|
5
5
|
export * from './tenant-slug.util';
|
|
6
|
+
export * from './anamnesis-public-frontend-url';
|
|
6
7
|
export * from './tenant-ai-token-state.dto';
|
|
7
8
|
export * from './consume-tenant-ai-tokens.dto';
|
|
8
9
|
export * from './recharge-tenant-ai-tokens.dto';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tenants/index.ts"],"names":[],"mappings":"AAAA,cAAc,qCAAqC,CAAC;AACpD,cAAc,mCAAmC,CAAC;AAClD,cAAc,yCAAyC,CAAC;AACxD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,oBAAoB,CAAC;AACnC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,wCAAwC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tenants/index.ts"],"names":[],"mappings":"AAAA,cAAc,qCAAqC,CAAC;AACpD,cAAc,mCAAmC,CAAC;AAClD,cAAc,yCAAyC,CAAC;AACxD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,oBAAoB,CAAC;AACnC,cAAc,iCAAiC,CAAC;AAChD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,wCAAwC,CAAC"}
|
package/dist/tenants/index.js
CHANGED
|
@@ -19,6 +19,7 @@ __exportStar(require("./get-tenant-by-address.query.dto"), exports);
|
|
|
19
19
|
__exportStar(require("./tenant-public-by-address-response.dto"), exports);
|
|
20
20
|
__exportStar(require("./whatsapp-provider-kind.dto"), exports);
|
|
21
21
|
__exportStar(require("./tenant-slug.util"), exports);
|
|
22
|
+
__exportStar(require("./anamnesis-public-frontend-url"), exports);
|
|
22
23
|
__exportStar(require("./tenant-ai-token-state.dto"), exports);
|
|
23
24
|
__exportStar(require("./consume-tenant-ai-tokens.dto"), exports);
|
|
24
25
|
__exportStar(require("./recharge-tenant-ai-tokens.dto"), exports);
|
package/package.json
CHANGED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {
|
|
2
|
+
hostnameMatchesTenantSlug,
|
|
3
|
+
resolveTenantScopedFrontendBaseUrl,
|
|
4
|
+
} from './anamnesis-public-frontend-url';
|
|
5
|
+
|
|
6
|
+
describe('resolveTenantScopedFrontendBaseUrl', () => {
|
|
7
|
+
it('keeps tenant-specific tychat.app host', () => {
|
|
8
|
+
expect(
|
|
9
|
+
resolveTenantScopedFrontendBaseUrl(
|
|
10
|
+
'clinica_sampaio',
|
|
11
|
+
'https://clinica-sampaio.tychat.app',
|
|
12
|
+
),
|
|
13
|
+
).toBe('https://clinica-sampaio.tychat.app');
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('scopes shared QA host with tenant subdomain', () => {
|
|
17
|
+
expect(
|
|
18
|
+
resolveTenantScopedFrontendBaseUrl('clinica_sampaio', 'https://qa.tychat.app'),
|
|
19
|
+
).toBe('https://clinica-sampaio.qa.tychat.app');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('keeps custom domain unchanged', () => {
|
|
23
|
+
expect(
|
|
24
|
+
resolveTenantScopedFrontendBaseUrl(
|
|
25
|
+
'clinica_sampaio',
|
|
26
|
+
'https://app.minhaclinica.com.br',
|
|
27
|
+
),
|
|
28
|
+
).toBe('https://app.minhaclinica.com.br');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('uses slug fallback when host is empty', () => {
|
|
32
|
+
expect(resolveTenantScopedFrontendBaseUrl('homolog', '')).toBe(
|
|
33
|
+
'https://homolog.tychat.app',
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe('hostnameMatchesTenantSlug', () => {
|
|
39
|
+
it('matches first DNS label to slug segment', () => {
|
|
40
|
+
expect(hostnameMatchesTenantSlug('clinica-sampaio.qa.tychat.app', 'clinica_sampaio')).toBe(
|
|
41
|
+
true,
|
|
42
|
+
);
|
|
43
|
+
expect(hostnameMatchesTenantSlug('qa.tychat.app', 'clinica_sampaio')).toBe(false);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { slugToDomainSegment } from './tenant-slug.util';
|
|
2
|
+
|
|
3
|
+
const TYCHAT_APP_SUFFIX = '.tychat.app';
|
|
4
|
+
|
|
5
|
+
function normalizeFrontendBaseUrl(raw: string): string {
|
|
6
|
+
const trimmed = raw.trim().replace(/\/+$/, '');
|
|
7
|
+
if (!trimmed) return '';
|
|
8
|
+
if (/^https?:\/\//i.test(trimmed)) {
|
|
9
|
+
return trimmed;
|
|
10
|
+
}
|
|
11
|
+
return `https://${trimmed}`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function firstHostnameLabel(hostname: string): string {
|
|
15
|
+
return hostname.toLowerCase().split('.').filter(Boolean)[0] ?? '';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** True when the hostname's first label matches the tenant slug segment. */
|
|
19
|
+
export function hostnameMatchesTenantSlug(hostname: string, tenantSlug: string): boolean {
|
|
20
|
+
const segment = slugToDomainSegment(tenantSlug);
|
|
21
|
+
if (!segment || segment === 'default') return false;
|
|
22
|
+
return firstHostnameLabel(hostname) === segment;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Builds a frontend base URL that resolves to a single tenant on public routes.
|
|
27
|
+
*
|
|
28
|
+
* Shared hosts (e.g. `qa.tychat.app`) are scoped as `{slug}.qa.tychat.app`.
|
|
29
|
+
* Custom domains and tenant-specific `*.tychat.app` hosts are kept as configured.
|
|
30
|
+
*/
|
|
31
|
+
export function resolveTenantScopedFrontendBaseUrl(
|
|
32
|
+
tenantSlug: string,
|
|
33
|
+
configuredHostRaw: string | null | undefined,
|
|
34
|
+
): string {
|
|
35
|
+
const slug = tenantSlug?.trim();
|
|
36
|
+
if (!slug) return '';
|
|
37
|
+
|
|
38
|
+
const segment = slugToDomainSegment(slug);
|
|
39
|
+
const fallback = `${segment}${TYCHAT_APP_SUFFIX}`;
|
|
40
|
+
const configured = configuredHostRaw?.trim();
|
|
41
|
+
const base = normalizeFrontendBaseUrl(configured || fallback);
|
|
42
|
+
|
|
43
|
+
let url: URL;
|
|
44
|
+
try {
|
|
45
|
+
url = new URL(base);
|
|
46
|
+
} catch {
|
|
47
|
+
return normalizeFrontendBaseUrl(fallback);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const hostname = url.hostname.toLowerCase();
|
|
51
|
+
if (hostnameMatchesTenantSlug(hostname, slug)) {
|
|
52
|
+
return url.origin;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (hostname.endsWith(TYCHAT_APP_SUFFIX)) {
|
|
56
|
+
url.hostname = `${segment}.${hostname}`;
|
|
57
|
+
return url.origin;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return url.origin;
|
|
61
|
+
}
|
package/src/tenants/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ export * from './get-tenant-by-address.query.dto';
|
|
|
3
3
|
export * from './tenant-public-by-address-response.dto';
|
|
4
4
|
export * from './whatsapp-provider-kind.dto';
|
|
5
5
|
export * from './tenant-slug.util';
|
|
6
|
+
export * from './anamnesis-public-frontend-url';
|
|
6
7
|
export * from './tenant-ai-token-state.dto';
|
|
7
8
|
export * from './consume-tenant-ai-tokens.dto';
|
|
8
9
|
export * from './recharge-tenant-ai-tokens.dto';
|