@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.
Files changed (31) hide show
  1. package/package.json +7 -7
  2. package/src/lib/server/ga4-measurement-protocol.js +41 -28
  3. package/src/lib/server/ga4-measurement-protocol.js.map +1 -1
  4. package/src/lib/server/host-visitor-records.d.ts +13 -0
  5. package/src/lib/server/host-visitor-records.js +22 -4
  6. package/src/lib/server/host-visitor-records.js.map +1 -1
  7. package/src/lib/server/hosted-page-review.js +6 -3
  8. package/src/lib/server/hosted-page-review.js.map +1 -1
  9. package/src/lib/server/outbound-send-review.js +6 -3
  10. package/src/lib/server/outbound-send-review.js.map +1 -1
  11. package/src/lib/server/payment-fraud-signal.d.ts +49 -9
  12. package/src/lib/server/payment-fraud-signal.js +192 -48
  13. package/src/lib/server/payment-fraud-signal.js.map +1 -1
  14. package/src/lib/server/render-monitor.d.ts +233 -0
  15. package/src/lib/server/render-monitor.js +573 -0
  16. package/src/lib/server/render-monitor.js.map +1 -0
  17. package/src/lib/server/risk-notice.d.ts +7 -0
  18. package/src/lib/server/risk-notice.js +6 -0
  19. package/src/lib/server/risk-notice.js.map +1 -1
  20. package/src/lib/server/web-risk.d.ts +2 -0
  21. package/src/lib/server/web-risk.js +18 -21
  22. package/src/lib/server/web-risk.js.map +1 -1
  23. package/src/tenancy.d.ts +0 -2
  24. package/src/tenancy.js +0 -2
  25. package/src/tenancy.js.map +1 -1
  26. package/src/lib/server/crm-email-activity.d.ts +0 -118
  27. package/src/lib/server/crm-email-activity.js +0 -148
  28. package/src/lib/server/crm-email-activity.js.map +0 -1
  29. package/src/lib/server/crm-next-activity.d.ts +0 -74
  30. package/src/lib/server/crm-next-activity.js +0 -194
  31. 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 {};