@aglyn/tenant-data-admin 1.0.0-beta.222 → 1.0.0-beta.224
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/package.json +7 -7
- package/src/lib/server/ga4-measurement-protocol.js +41 -28
- package/src/lib/server/ga4-measurement-protocol.js.map +1 -1
- package/src/lib/server/host-visitor-records.d.ts +13 -0
- package/src/lib/server/host-visitor-records.js +22 -4
- package/src/lib/server/host-visitor-records.js.map +1 -1
- package/src/lib/server/hosted-page-review.js +6 -3
- package/src/lib/server/hosted-page-review.js.map +1 -1
- package/src/lib/server/outbound-send-review.js +6 -3
- package/src/lib/server/outbound-send-review.js.map +1 -1
- package/src/lib/server/payment-fraud-signal.d.ts +49 -9
- package/src/lib/server/payment-fraud-signal.js +192 -48
- package/src/lib/server/payment-fraud-signal.js.map +1 -1
- package/src/lib/server/render-monitor.d.ts +233 -0
- package/src/lib/server/render-monitor.js +573 -0
- package/src/lib/server/render-monitor.js.map +1 -0
- package/src/lib/server/risk-notice.d.ts +7 -0
- package/src/lib/server/risk-notice.js +6 -0
- package/src/lib/server/risk-notice.js.map +1 -1
- package/src/lib/server/web-risk.d.ts +2 -0
- package/src/lib/server/web-risk.js +18 -21
- package/src/lib/server/web-risk.js.map +1 -1
- package/src/tenancy.d.ts +0 -2
- package/src/tenancy.js +0 -2
- package/src/tenancy.js.map +1 -1
- package/src/lib/server/crm-email-activity.d.ts +0 -118
- package/src/lib/server/crm-email-activity.js +0 -148
- package/src/lib/server/crm-email-activity.js.map +0 -1
- package/src/lib/server/crm-next-activity.d.ts +0 -74
- package/src/lib/server/crm-next-activity.js +0 -194
- package/src/lib/server/crm-next-activity.js.map +0 -1
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import type { OperatorHealthStateDoc } from './operator-health';
|
|
18
|
+
/** Every render monitor document id starts with this. */
|
|
19
|
+
export declare const RENDER_MONITOR_CHECK_PREFIX = "render-monitor--";
|
|
20
|
+
/** The beat id `/api/health/crons` reads for the scheduled run. */
|
|
21
|
+
export declare const RENDER_MONITOR_JOB_ID = "render-monitor";
|
|
22
|
+
/**
|
|
23
|
+
* How long one page fetch may take. Well under the 60 s a platform function
|
|
24
|
+
* may run, so a hung render is graded here as a timeout rather than waiting
|
|
25
|
+
* for the platform's 504, and well under the route's own `maxDuration`.
|
|
26
|
+
*/
|
|
27
|
+
export declare const RENDER_MONITOR_TIMEOUT_MS = 25000;
|
|
28
|
+
/** Failing runs in a row before the alert goes out. */
|
|
29
|
+
export declare const RENDER_MONITOR_DEFAULT_THRESHOLD = 2;
|
|
30
|
+
/**
|
|
31
|
+
* The prefix of the never-requested path the `isr` probe asks for. A
|
|
32
|
+
* lowercase slug on purpose: it must reach the catch-all page like any
|
|
33
|
+
* screen slug would, not a reserved or rewritten route.
|
|
34
|
+
*/
|
|
35
|
+
export declare const RENDER_PROBE_ISR_PREFIX = "/aglyn-render-probe-";
|
|
36
|
+
export type RenderProbeKind = 'dynamic' | 'isr' | 'page';
|
|
37
|
+
export interface RenderProbeSpec {
|
|
38
|
+
kind: RenderProbeKind;
|
|
39
|
+
/** The path to request, given a fresh nonce. */
|
|
40
|
+
path: (nonce: string) => string;
|
|
41
|
+
/** The status a healthy render answers with. */
|
|
42
|
+
expectStatus: number;
|
|
43
|
+
}
|
|
44
|
+
export declare const RENDER_PROBES: readonly RenderProbeSpec[];
|
|
45
|
+
/** One fetched response, reduced to what grading reads. */
|
|
46
|
+
export interface RenderProbeResponse {
|
|
47
|
+
status: number;
|
|
48
|
+
contentType?: string | null;
|
|
49
|
+
body?: string;
|
|
50
|
+
location?: string | null;
|
|
51
|
+
/** `x-vercel-cache` on Vercel, `x-nextjs-cache` on a plain `next start`. */
|
|
52
|
+
cacheState?: string | null;
|
|
53
|
+
}
|
|
54
|
+
export interface RenderProbeVerdict {
|
|
55
|
+
ok: boolean;
|
|
56
|
+
/** Bot protection answered instead of the site. */
|
|
57
|
+
challenged: boolean;
|
|
58
|
+
/** A stable code; `ok` on a pass. */
|
|
59
|
+
code: string;
|
|
60
|
+
detail: string;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Grade one probe response. Pure.
|
|
64
|
+
*
|
|
65
|
+
* The page markers are the front doors' (`tools/scripts/lib/front-door.mjs`)
|
|
66
|
+
* and for their reason: structural, never site copy. `</html>` proves the
|
|
67
|
+
* document finished streaming, `/_next/static/` that our build produced it.
|
|
68
|
+
*/
|
|
69
|
+
export declare function gradeRenderProbe(response: RenderProbeResponse, expectStatus: number): RenderProbeVerdict;
|
|
70
|
+
/**
|
|
71
|
+
* The pages Aglyn's own console renders fresh when `RENDER_MONITOR_PAGES` is
|
|
72
|
+
* unset: the canary's real client pages (`tools/scripts/lib/prod-canary.mjs`
|
|
73
|
+
* `DEFAULT_TENANT_HOSTS`), images, reusable components, repeats and a form
|
|
74
|
+
* between them. Applied ONLY on that console deployment (`VERCEL_PROJECT_ID`),
|
|
75
|
+
* so no other install ever requests them.
|
|
76
|
+
*/
|
|
77
|
+
export declare const PLATFORM_RENDER_PAGES: string;
|
|
78
|
+
/** Aglyn's console project on Vercel, the one deployment the list above is for. */
|
|
79
|
+
export declare const PLATFORM_CONSOLE_VERCEL_PROJECT_ID = "prj_gEzxEXc0Lhs81rmaXIg2a1GbsDfl";
|
|
80
|
+
/**
|
|
81
|
+
* The tenant's production project domain on Vercel: follows every promote and
|
|
82
|
+
* rollback, honors `?tenantHost=`, and is reachable only with the bypass.
|
|
83
|
+
*/
|
|
84
|
+
export declare const PLATFORM_RENDER_ORIGIN = "https://aglyn-tenant-aglyn.vercel.app";
|
|
85
|
+
/**
|
|
86
|
+
* Spelling number `n` of one site: a `?tenantHost=` value that resolves to
|
|
87
|
+
* that site and is a `[host]` route segment no other number produces, so the
|
|
88
|
+
* page it asks for has no cache entry to come from.
|
|
89
|
+
*
|
|
90
|
+
* - `{sub}.{apex}` → the full name, letters upper-cased by the bits of `n`,
|
|
91
|
+
* then one trailing dot per exhausted round of cases.
|
|
92
|
+
* - any other name → `cname--{name}`, recased the same way and cycling after
|
|
93
|
+
* `2 ** letters` (that form keeps trailing dots), with the prefix left
|
|
94
|
+
* lower-case because the canonical-domain redirect tests the raw segment.
|
|
95
|
+
*
|
|
96
|
+
* MUST stay identical to `freshHostSpelling` in
|
|
97
|
+
* `tools/scripts/lib/prod-canary.mjs`; `apps/tenant/specs/probe-host-spelling.spec.ts`
|
|
98
|
+
* pins both against `normalizeHostAlias`.
|
|
99
|
+
*/
|
|
100
|
+
export declare function freshHostSpelling(host: string, n: number, apex?: string): string;
|
|
101
|
+
/**
|
|
102
|
+
* The spelling number for a run at `nowMs`: minutes since 2026-10-01, so it
|
|
103
|
+
* never repeats on one deployment — a `{sub}.aglyn.app` name of 18 letters
|
|
104
|
+
* gains its first trailing dot after half a year.
|
|
105
|
+
*/
|
|
106
|
+
export declare function renderSpellingNumber(nowMs: number): number;
|
|
107
|
+
/** One watched site's real pages. */
|
|
108
|
+
export interface RenderMonitorPages {
|
|
109
|
+
/** The site's public origin, which keys its state document. */
|
|
110
|
+
origin: string;
|
|
111
|
+
host: string;
|
|
112
|
+
paths: string[];
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* `"a.example.app b.example.app/services, https://b.example.app/contact"` →
|
|
116
|
+
* one entry per host, paths merged, `/` when an entry names none. Entries that
|
|
117
|
+
* are not a hostname are dropped, as `RENDER_MONITOR_ORIGINS` drops them.
|
|
118
|
+
*/
|
|
119
|
+
export declare function parseRenderMonitorPages(text: string | undefined | null): RenderMonitorPages[];
|
|
120
|
+
/**
|
|
121
|
+
* Which real pages the monitor renders, and through which origin.
|
|
122
|
+
*
|
|
123
|
+
* `RENDER_MONITOR_PAGES` (same grammar as the canary's `CANARY_TENANT_HOSTS`)
|
|
124
|
+
* and `RENDER_MONITOR_RENDER_ORIGIN`, else — on Aglyn's own console only —
|
|
125
|
+
* `PLATFORM_RENDER_PAGES` through `PLATFORM_RENDER_ORIGIN`. `off` (here or in
|
|
126
|
+
* `RENDER_MONITOR_ORIGINS`) renders none. No render origin, no pages.
|
|
127
|
+
*/
|
|
128
|
+
export declare function resolveRenderMonitorPages(env: Readonly<Record<string, string | undefined>>): {
|
|
129
|
+
renderOrigin: string;
|
|
130
|
+
pages: RenderMonitorPages[];
|
|
131
|
+
};
|
|
132
|
+
/** A configured value as an origin, or '' when it is not one. */
|
|
133
|
+
export declare function originOf(value: string | undefined | null): string;
|
|
134
|
+
/**
|
|
135
|
+
* Which sites the monitor renders, from the console's environment.
|
|
136
|
+
*
|
|
137
|
+
* 1. The install's demonstration site: `OPERATOR_HEALTH_TENANT_ORIGIN` when
|
|
138
|
+
* set (the one published site the operator alerts tick already watches),
|
|
139
|
+
* else `https://<AGLYN_TENANT_DEMO or demo>.<tenant apex>`.
|
|
140
|
+
* 2. Plus every origin in `RENDER_MONITOR_ORIGINS`, comma separated.
|
|
141
|
+
*
|
|
142
|
+
* `RENDER_MONITOR_ORIGINS=off` watches nothing, for an install with no
|
|
143
|
+
* published site to watch. Values that are not origins are dropped.
|
|
144
|
+
*/
|
|
145
|
+
export declare function resolveRenderMonitorTargets(env: Readonly<Record<string, string | undefined>>): string[];
|
|
146
|
+
/** The state document id for one origin. */
|
|
147
|
+
export declare function renderMonitorCheckId(origin: string): string;
|
|
148
|
+
/** One render monitor state document. */
|
|
149
|
+
export interface RenderMonitorStateDoc extends OperatorHealthStateDoc {
|
|
150
|
+
origin: string;
|
|
151
|
+
/** Failing runs in a row, reset by any passing run. */
|
|
152
|
+
consecutiveFailures: number;
|
|
153
|
+
/** When the current run of failures began; null while passing. */
|
|
154
|
+
failingSinceMs: number | null;
|
|
155
|
+
lastOkAtMs: number | null;
|
|
156
|
+
}
|
|
157
|
+
export type RenderMonitorTransition = 'failing' | 'recovered' | null;
|
|
158
|
+
/**
|
|
159
|
+
* The next state for one site, given what this run saw. Pure.
|
|
160
|
+
*
|
|
161
|
+
* The status turns `degraded` only once `threshold` runs in a row have
|
|
162
|
+
* failed, so one slow cold start or one dropped connection says nothing. It
|
|
163
|
+
* turns back on the first passing run, because a render that works is
|
|
164
|
+
* proof, and a recovery held back is a recovery nobody hears about.
|
|
165
|
+
*/
|
|
166
|
+
export declare function nextRenderMonitorState(prior: Partial<RenderMonitorStateDoc> | null, observation: {
|
|
167
|
+
ok: boolean;
|
|
168
|
+
detail: string;
|
|
169
|
+
}, context: {
|
|
170
|
+
origin: string;
|
|
171
|
+
label: string;
|
|
172
|
+
threshold: number;
|
|
173
|
+
now: number;
|
|
174
|
+
}): {
|
|
175
|
+
next: RenderMonitorStateDoc;
|
|
176
|
+
transition: RenderMonitorTransition;
|
|
177
|
+
};
|
|
178
|
+
/** One probe as the report shows it. */
|
|
179
|
+
export interface RenderProbeRow extends RenderProbeVerdict {
|
|
180
|
+
kind: RenderProbeKind;
|
|
181
|
+
url: string;
|
|
182
|
+
ms: number;
|
|
183
|
+
httpStatus: number | null;
|
|
184
|
+
}
|
|
185
|
+
/** One site's result for a run. */
|
|
186
|
+
export interface RenderMonitorSiteResult {
|
|
187
|
+
origin: string;
|
|
188
|
+
ok: boolean;
|
|
189
|
+
probes: RenderProbeRow[];
|
|
190
|
+
/** The state written; absent on a dry run or a failed write. */
|
|
191
|
+
status?: 'ok' | 'degraded';
|
|
192
|
+
consecutiveFailures?: number;
|
|
193
|
+
transition?: RenderMonitorTransition;
|
|
194
|
+
error?: string;
|
|
195
|
+
}
|
|
196
|
+
export interface RenderMonitorReport {
|
|
197
|
+
dryRun: boolean;
|
|
198
|
+
threshold: number;
|
|
199
|
+
sites: RenderMonitorSiteResult[];
|
|
200
|
+
}
|
|
201
|
+
/** The read-modify-write of one site's state, as one transaction. */
|
|
202
|
+
export type RenderMonitorStore = <T>(checkId: string, work: (prior: Partial<RenderMonitorStateDoc> | null) => {
|
|
203
|
+
next: RenderMonitorStateDoc;
|
|
204
|
+
result: T;
|
|
205
|
+
}) => Promise<T>;
|
|
206
|
+
type RaiseAlert = (type: string, options: {
|
|
207
|
+
dedupeKey?: string;
|
|
208
|
+
context?: Record<string, string | number>;
|
|
209
|
+
}) => Promise<unknown>;
|
|
210
|
+
/**
|
|
211
|
+
* The `page` probe's URL for one run: this run's page of the site's list
|
|
212
|
+
* (rotating every five-minute slot), under this run's spelling of the site.
|
|
213
|
+
*/
|
|
214
|
+
export declare function pageProbeUrl(renderOrigin: string, site: RenderMonitorPages, slot: number, spelling: number): string;
|
|
215
|
+
/**
|
|
216
|
+
* One run: render every target, record each site's state, raise the edge.
|
|
217
|
+
*
|
|
218
|
+
* A dry run fetches and grades and writes nothing, so a person can ask
|
|
219
|
+
* "what would the monitor see right now" without moving its state.
|
|
220
|
+
*/
|
|
221
|
+
export declare function runRenderMonitor(options?: {
|
|
222
|
+
dryRun?: boolean;
|
|
223
|
+
env?: Readonly<Record<string, string | undefined>>;
|
|
224
|
+
fetcher?: typeof fetch;
|
|
225
|
+
/** Our own edge's bypass headers, from the caller's environment. */
|
|
226
|
+
headers?: Record<string, string>;
|
|
227
|
+
now?: () => number;
|
|
228
|
+
nonce?: () => string;
|
|
229
|
+
timeoutMs?: number;
|
|
230
|
+
store?: RenderMonitorStore;
|
|
231
|
+
raise?: RaiseAlert;
|
|
232
|
+
}): Promise<RenderMonitorReport>;
|
|
233
|
+
export {};
|