@teamscala/service-boot 0.23.319 → 0.23.321
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 +526 -526
- package/src/agreement-gate.d.ts +19 -0
- package/src/agreement-gate.ts +0 -12
- package/src/asset-cache-key.d.ts +11 -0
- package/src/asset-optimize.d.ts +17 -0
- package/src/asset-proxy.d.ts +11 -0
- package/src/asset-social-card.d.ts +17 -0
- package/src/attach-agreement-magic.d.ts +20 -0
- package/src/attach-portal-members.d.ts +23 -0
- package/src/attach-proposal-accept.d.ts +26 -0
- package/src/attach-proposal-magic.d.ts +18 -0
- package/src/attach-transfers-upload.d.ts +27 -0
- package/src/auth-gate.d.ts +28 -0
- package/src/auth-gate.ts +6 -68
- package/src/bind-page-handler.d.ts +18 -0
- package/src/build-client-assets/input-hashing.d.ts +25 -0
- package/src/build-client-assets/source-candidates.d.ts +27 -0
- package/src/build-client-assets.d.ts +50 -0
- package/src/build-client-assets.test.ts +1 -1
- package/src/build-client-assets.ts +1 -1
- package/src/compose-page-title.d.ts +41 -0
- package/src/content-gate/access.d.ts +15 -0
- package/src/content-gate/config-types.d.ts +99 -0
- package/src/content-gate/gate.d.ts +13 -0
- package/src/content-gate/parse-cookie.d.ts +9 -0
- package/src/content-gate/token.d.ts +48 -0
- package/src/content-page-floor.d.ts +39 -0
- package/src/discover-blocks.d.ts +28 -0
- package/src/discover-blocks.ts +1 -1
- package/src/expand-roster.d.ts +34 -0
- package/src/fn-routers/agreement-completion-email.d.ts +14 -0
- package/src/fn-routers/agreement-send-router.d.ts +82 -0
- package/src/fn-routers/ai-health-router.d.ts +35 -0
- package/src/fn-routers/auth-router.d.ts +47 -0
- package/src/fn-routers/cache-admin-router.d.ts +57 -0
- package/src/fn-routers/chat-session-router.d.ts +219 -0
- package/src/fn-routers/email-defaults.d.ts +1 -0
- package/src/fn-routers/email-link-ttl.d.ts +1 -0
- package/src/fn-routers/index.d.ts +18 -0
- package/src/fn-routers/index.ts +0 -2
- package/src/fn-routers/panel-data-router.d.ts +19 -0
- package/src/fn-routers/persist-chat-messages.d.ts +36 -0
- package/src/fn-routers/plans-router.d.ts +25 -0
- package/src/fn-routers/proposal-send-router.d.ts +58 -0
- package/src/fn-routers/public-agreement-router.d.ts +289 -0
- package/src/fn-routers/public-assessment-router.d.ts +68 -0
- package/src/fn-routers/public-gpt-session-router.d.ts +143 -0
- package/src/fn-routers/sign-in-flow-router.d.ts +107 -0
- package/src/fn-routers/task-sync-assign.d.ts +305 -0
- package/src/fn-routers/task-sync-assign.test.ts +1 -2
- package/src/fn-routers/task-sync-assign.ts +4 -4
- package/src/fn-routers/task-sync-bind.test.ts +18 -18
- package/src/fn-routers/task-sync-brief.d.ts +96 -0
- package/src/fn-routers/task-sync-brief.test.ts +17 -20
- package/src/fn-routers/task-sync-brief.ts +14 -15
- package/src/fn-routers/task-sync-dispatch.d.ts +61 -0
- package/src/fn-routers/task-sync-dispatch.test.ts +33 -51
- package/src/fn-routers/task-sync-dispatch.ts +124 -0
- package/src/fn-routers/task-sync-lane.d.ts +49 -0
- package/src/fn-routers/task-sync-lane.ts +125 -0
- package/src/fn-routers/task-sync-projection.d.ts +121 -0
- package/src/fn-routers/task-sync-projection.test.ts +0 -26
- package/src/fn-routers/task-sync-projection.ts +0 -51
- package/src/fn-routers/task-sync-router.d.ts +377 -0
- package/src/fn-routers/task-sync-router.test.ts +2 -14
- package/src/fn-routers/task-sync-router.ts +1075 -0
- package/src/fn-routers/tasks-router.d.ts +25 -0
- package/src/fn-routers/user-preferences-router.d.ts +77 -0
- package/src/fn-routers/work-items-router.d.ts +102 -0
- package/src/gate-ttls.d.ts +6 -0
- package/src/gpt-gate.d.ts +25 -0
- package/src/handlers/bun-stable-watch.d.ts +1 -0
- package/src/handlers/pane-outbox-drain.d.ts +1 -0
- package/src/handlers/projects-digest.d.ts +26 -0
- package/src/handlers/prune-stale-data.d.ts +30 -0
- package/src/handlers/registry-stale-inactive-sweep.d.ts +1 -0
- package/src/handlers/scala-tools-currency-watch.d.ts +1 -0
- package/src/handlers/singleton-tools-currency-watch.d.ts +1 -0
- package/src/handlers/singleton-tools-currency-watch.ts +11 -114
- package/src/handlers/sweep-event-log.d.ts +1 -0
- package/src/handlers/sweep-host-cache.d.ts +1 -0
- package/src/handlers/sweep-host-cache.ts +26 -143
- package/src/handlers/upstream-issue-sync.d.ts +1 -0
- package/src/handlers/whatsapp-liveness-logic.d.ts +34 -0
- package/src/handlers/whatsapp-liveness.d.ts +4 -0
- package/src/hono-error-handler.d.ts +25 -0
- package/src/install-harness.d.ts +45 -0
- package/src/install-logging.d.ts +34 -0
- package/src/install-logging.ts +0 -3
- package/src/load-service-install-row.d.ts +24 -0
- package/src/load-service-prisma-client.d.ts +18 -0
- package/src/mount-orpc-router.d.ts +15 -0
- package/src/mount-orpc-router.ts +1 -1
- package/src/mount-server-packages.d.ts +29 -0
- package/src/mount.d.ts +9 -0
- package/src/mutable-app-ref.d.ts +44 -0
- package/src/page-route-shell-types.d.ts +12 -0
- package/src/page-server-orpc/audit-revert-router.d.ts +37 -0
- package/src/page-server-orpc/hub-mcp-client.d.ts +12 -0
- package/src/page-server-orpc/portal-diagnostics-router.d.ts +49 -0
- package/src/page-server-orpc/puck-pages-router.d.ts +58 -0
- package/src/page-server-orpc/server-client.d.ts +16 -0
- package/src/page-server-orpc/templates-router.d.ts +34 -0
- package/src/page-server-orpc/user-preferences-router.d.ts +79 -0
- package/src/pg-boss-handler-registry.d.ts +27 -0
- package/src/pg-boss-mount.d.ts +42 -0
- package/src/proposal-authorized-emails.d.ts +28 -0
- package/src/proposal-chat-inject.d.ts +17 -0
- package/src/proposal-gate.d.ts +18 -0
- package/src/read-html-template.d.ts +38 -0
- package/src/read-viewport-from-request.d.ts +18 -0
- package/src/readiness-gate.d.ts +16 -0
- package/src/register-api-routes.d.ts +18 -0
- package/src/register-crash-handlers.d.ts +15 -0
- package/src/register-page-routes.d.ts +27 -0
- package/src/register-page-routes.ts +3 -0
- package/src/register-page-server-client.d.ts +11 -0
- package/src/register-registry-page-route/ai-chat-prefetch.d.ts +13 -0
- package/src/register-registry-page-route/graph-cache.d.ts +30 -0
- package/src/register-registry-page-route/meta-title.d.ts +2 -0
- package/src/register-registry-page-route/proposal-approval-email-token.d.ts +14 -0
- package/src/register-registry-page-route/proposal-brand-stamping.d.ts +28 -0
- package/src/register-registry-page-route/resolve-page-data.d.ts +1 -0
- package/src/register-registry-page-route.d.ts +27 -0
- package/src/register-static-assets.d.ts +13 -0
- package/src/register-ws-routes.d.ts +28 -0
- package/src/resolve-app-base-url.d.ts +13 -0
- package/src/resolve-brand-favicon.d.ts +28 -0
- package/src/resolve-brand-favicon.ts +1 -1
- package/src/resolve-from-loader-config.d.ts +32 -0
- package/src/resolve-service-context.d.ts +17 -0
- package/src/resolve-service-port.d.ts +75 -0
- package/src/resolve-theme-mode.d.ts +42 -0
- package/src/resolvers/fetch-org-brand.d.ts +2 -0
- package/src/resolvers/portal-route-paths.d.ts +39 -0
- package/src/resolvers/website-by-host-map.d.ts +12 -0
- package/src/resource-chat-inject.d.ts +46 -0
- package/src/resource-not-found.d.ts +60 -0
- package/src/resource-not-found.ts +0 -10
- package/src/run-boot-hook-list.d.ts +19 -0
- package/src/run-boot-hooks.d.ts +31 -0
- package/src/scala-tools-currency-watchdog.d.ts +10 -0
- package/src/security-headers.d.ts +12 -0
- package/src/serve/serve.d.ts +46 -0
- package/src/serve/stamp-transport-identity.d.ts +32 -0
- package/src/serve/stamp-transport-identity.ts +2 -14
- package/src/server-error-page.d.ts +29 -0
- package/src/start-scheduled-jobs.d.ts +18 -0
- package/src/two-phase-boot.d.ts +136 -0
- package/src/two-phase-boot.ts +17 -85
- package/src/two-phase-health-gate.test.ts +2 -13
- package/src/types.d.ts +46 -0
- package/src/ui-service.d.ts +46 -0
- package/src/ws-boot-self-test.d.ts +19 -0
- package/src/auth-gate.test.ts +0 -70
- package/src/fn-routers/task-sync-close.test.ts +0 -44
- package/src/fn-routers/task-sync-close.ts +0 -82
- package/src/fn-routers/task-sync-hook-lifecycle.test.ts +0 -204
- package/src/fn-routers/task-sync-lane.test.ts +0 -36
- package/src/fn-routers/task-sync-rename.test.ts +0 -53
- package/src/fn-routers/task-sync-rename.ts +0 -111
- package/src/handlers/sweep-host-cache.test.ts +0 -133
- package/src/serve/stamp-transport-identity.test.ts +0 -24
- package/src/two-phase-health-gate-client-only.test.ts +0 -77
|
@@ -16,14 +16,8 @@
|
|
|
16
16
|
* Path convention (config.retention keys, substituted at runtime via `userHome()`
|
|
17
17
|
* / `workspaceRoot()` per `host-literals-via-substitution` — source never
|
|
18
18
|
* hardcodes host paths):
|
|
19
|
-
* "${HOME}/.bun/install/cache" → "30 days"
|
|
20
|
-
* "${HOME}/.config/*.retired-*" → "14 days" (glob leaf, optional)
|
|
21
|
-
*
|
|
22
|
-
* config.retention_dirs — whole-DIRECTORY prune for build caches (Rust
|
|
23
|
-
* `target/`) whose unit is the dir, not the file (file-by-mtime would corrupt
|
|
24
|
-
* the cache). Keys support `*` globs; a matched dir is deleted ENTIRELY only
|
|
25
|
-
* when its newest file is older than the cutoff:
|
|
26
|
-
* "${WORKSPACE}/<repo>/target" → "30 days" (glob wildcard, whole-dir delete)
|
|
19
|
+
* "${HOME}/.bun/install/cache" → "30 days"
|
|
20
|
+
* "${HOME}/.config/*.retired-*" → "14 days" (glob leaf, optional)
|
|
27
21
|
*
|
|
28
22
|
* Deletion runs through Bun fs APIs — pure filesystem deletion needs no shell,
|
|
29
23
|
* so `spawn-is-the-only-spawn` does not apply (that governs shell commands).
|
|
@@ -40,7 +34,7 @@
|
|
|
40
34
|
* Registered under slug `@teamscala/service-boot:sweep-host-cache`.
|
|
41
35
|
*/
|
|
42
36
|
import { rm, readdir, stat } from "node:fs/promises";
|
|
43
|
-
import {
|
|
37
|
+
import type { Dirent } from "node:fs";
|
|
44
38
|
import { join } from "node:path";
|
|
45
39
|
import { spawnSync } from "@teamscala/os/spawn/spawn";
|
|
46
40
|
import { userHome, workspaceRoot } from "@teamscala/os/host-paths";
|
|
@@ -123,97 +117,11 @@ async function sweepPath(
|
|
|
123
117
|
return { path: rootDir, scanned, deleted, bytesFreed };
|
|
124
118
|
}
|
|
125
119
|
|
|
126
|
-
/**
|
|
127
|
-
* Expand a path pattern containing single-segment `*` wildcards into concrete
|
|
128
|
-
* paths (files or dirs). Used by `retention_dirs` so a single config key like
|
|
129
|
-
* a pattern like `${WORKSPACE}/<star>/target` reaches every repo's target dir.
|
|
130
|
-
* Sync + bounded: the
|
|
131
|
-
* pattern is shallow (3 segments), and this runs once
|
|
132
|
-
* nightly. Non-existent segments yield no matches (the caller stats each result).
|
|
133
|
-
*/
|
|
134
|
-
export function expandGlob(pattern: string): string[] {
|
|
135
|
-
const absolute = pattern.startsWith("/");
|
|
136
|
-
const segments = (absolute ? pattern.slice(1) : pattern)
|
|
137
|
-
.split("/")
|
|
138
|
-
.filter((s) => s.length > 0);
|
|
139
|
-
let current: string[] = [absolute ? "/" : "."];
|
|
140
|
-
for (const seg of segments) {
|
|
141
|
-
if (current.length === 0) break;
|
|
142
|
-
const next: string[] = [];
|
|
143
|
-
for (const dir of current) {
|
|
144
|
-
let names: string[];
|
|
145
|
-
try {
|
|
146
|
-
names = readdirSync(dir);
|
|
147
|
-
} catch {
|
|
148
|
-
continue;
|
|
149
|
-
}
|
|
150
|
-
for (const name of names) {
|
|
151
|
-
if (seg === "*" || seg === name) next.push(join(dir, name));
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
current = next;
|
|
155
|
-
}
|
|
156
|
-
return current;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Is `dir` stale — i.e. NO file under it is newer than `cutoffMs`? Walks
|
|
161
|
-
* recursively and SHORT-CIRCUITS the instant it finds a fresh file (so an
|
|
162
|
-
* actively-built target dir returns fast without walking its full tree). On a
|
|
163
|
-
* stale dir the walk is complete and returns the total size + file count, so
|
|
164
|
-
* the caller can report bytes-freed before deleting the dir whole.
|
|
165
|
-
*/
|
|
166
|
-
export async function checkDirStale(
|
|
167
|
-
dir: string,
|
|
168
|
-
cutoffMs: number,
|
|
169
|
-
): Promise<{ stale: boolean; bytes: number; fileCount: number }> {
|
|
170
|
-
const stack: string[] = [dir];
|
|
171
|
-
let bytes = 0;
|
|
172
|
-
let fileCount = 0;
|
|
173
|
-
while (stack.length > 0) {
|
|
174
|
-
const d = stack.pop();
|
|
175
|
-
if (d === undefined) break;
|
|
176
|
-
let entries: Dirent[];
|
|
177
|
-
try {
|
|
178
|
-
entries = await readdir(d, { withFileTypes: true });
|
|
179
|
-
} catch {
|
|
180
|
-
continue;
|
|
181
|
-
}
|
|
182
|
-
for (const entry of entries) {
|
|
183
|
-
const full = join(d, entry.name);
|
|
184
|
-
if (entry.isDirectory()) {
|
|
185
|
-
stack.push(full);
|
|
186
|
-
} else if (entry.isFile()) {
|
|
187
|
-
try {
|
|
188
|
-
const st = await stat(full);
|
|
189
|
-
if (st.mtimeMs >= cutoffMs) {
|
|
190
|
-
return { stale: false, bytes: 0, fileCount: 0 };
|
|
191
|
-
}
|
|
192
|
-
bytes += st.size;
|
|
193
|
-
fileCount++;
|
|
194
|
-
} catch {
|
|
195
|
-
// vanished/unreadable — skip
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
return { stale: true, bytes, fileCount };
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/** Parse the root-fs Use% from `df --output=pcent,target` output (pure, for tests).
|
|
204
|
-
* Returns null if no root-mount line is found. */
|
|
205
|
-
export function parseRootDiskPct(out: string): number | null {
|
|
206
|
-
const rootLine = out.split("\n").find((l) => l.trim().endsWith("/"));
|
|
207
|
-
const m = rootLine?.match(/(\d+)%/);
|
|
208
|
-
return m ? Number(m[1]) : null;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
120
|
async function sweepHostCache(ctx: {
|
|
212
|
-
config: { retention?: Record<string, string
|
|
121
|
+
config: { retention?: Record<string, string> };
|
|
213
122
|
}): Promise<void> {
|
|
214
123
|
const log = getAppLogger();
|
|
215
124
|
const retention = ctx.config.retention ?? {};
|
|
216
|
-
const retentionDirs = ctx.config.retention_dirs ?? {};
|
|
217
125
|
const now = Date.now();
|
|
218
126
|
const results: SweepResult[] = [];
|
|
219
127
|
|
|
@@ -246,36 +154,6 @@ async function sweepHostCache(ctx: {
|
|
|
246
154
|
);
|
|
247
155
|
}
|
|
248
156
|
|
|
249
|
-
// retention_dirs — whole-directory prune for build caches (Rust target/).
|
|
250
|
-
// A matched dir is deleted ENTIRELY only when its newest file is older than
|
|
251
|
-
// the cutoff, so an actively-built repo keeps its cache and an abandoned one
|
|
252
|
-
// is reclaimed whole (never the partial slice that would corrupt the cache).
|
|
253
|
-
for (const [rawPattern, rawDuration] of Object.entries(retentionDirs)) {
|
|
254
|
-
const days = parseDays(rawDuration);
|
|
255
|
-
if (days === null || days <= 0) {
|
|
256
|
-
log.warn(
|
|
257
|
-
`[host-cache-sweep] skipping retention_dirs "${rawPattern}" — value "${rawDuration}" is not a day-granularity duration (expected e.g. "30 days")`,
|
|
258
|
-
);
|
|
259
|
-
continue;
|
|
260
|
-
}
|
|
261
|
-
const cutoffMs = now - days * MS_PER_DAY;
|
|
262
|
-
for (const dir of expandGlob(resolveHostPath(rawPattern))) {
|
|
263
|
-
try {
|
|
264
|
-
const st = await stat(dir);
|
|
265
|
-
if (!st.isDirectory()) continue;
|
|
266
|
-
} catch {
|
|
267
|
-
continue; // glob match that is not a dir (or vanished) — skip
|
|
268
|
-
}
|
|
269
|
-
const { stale, bytes, fileCount } = await checkDirStale(dir, cutoffMs);
|
|
270
|
-
if (!stale) continue;
|
|
271
|
-
await rm(dir, { recursive: true, force: true });
|
|
272
|
-
results.push({ path: dir, scanned: fileCount, deleted: fileCount, bytesFreed: bytes });
|
|
273
|
-
log.info(
|
|
274
|
-
`[host-cache-sweep] ${dir}: no build in ${rawDuration}; reclaimed whole dir — ${fileCount} file(s), ${formatBytes(bytes)}`,
|
|
275
|
-
);
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
157
|
const totalDeleted = results.reduce((sum, r) => sum + r.deleted, 0);
|
|
280
158
|
const totalBytes = results.reduce((sum, r) => sum + r.bytesFreed, 0);
|
|
281
159
|
log.info(
|
|
@@ -298,27 +176,32 @@ async function sweepHostCache(ctx: {
|
|
|
298
176
|
},
|
|
299
177
|
});
|
|
300
178
|
|
|
301
|
-
// Disk-threshold
|
|
302
|
-
//
|
|
303
|
-
//
|
|
304
|
-
//
|
|
305
|
-
|
|
306
|
-
// the nightly prune can absorb (a cache with no retention entry, an external
|
|
307
|
-
// dump, a runaway log) so the operator learns it within a day, not at 100%.
|
|
308
|
-
const diskHighPct = 90;
|
|
179
|
+
// Disk-threshold monitor — piggybacks on this nightly sweep event (no separate
|
|
180
|
+
// interval/infra: `guards-never-run-on-interval`). If `/` usage crosses the
|
|
181
|
+
// configured threshold, emit `host.disk.high` (-> event-log/GlitchTip) so a fill
|
|
182
|
+
// the sweep can't absorb (external dump, runaway log) is caught within a day.
|
|
183
|
+
const highPercent = 90;
|
|
309
184
|
try {
|
|
310
|
-
const dfRes = spawnSync({ name: "df:/", command: ["df", "--output=pcent,target"], timeoutMs:
|
|
185
|
+
const dfRes = spawnSync({ name: "df:/", command: ["df", "--output=pcent,target"], timeoutMs: 5000 });
|
|
311
186
|
const dfOut = (dfRes.stdout ?? "").trim();
|
|
312
|
-
const
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
187
|
+
const rootLine = dfOut
|
|
188
|
+
.split("\n")
|
|
189
|
+
.find((l) => l.trim().endsWith("/"));
|
|
190
|
+
const pctMatch = rootLine?.match(/(\d+)%/);
|
|
191
|
+
if (pctMatch) {
|
|
192
|
+
const usePct = Number(pctMatch[1]);
|
|
193
|
+
if (usePct >= highPercent) {
|
|
194
|
+
log.warn(
|
|
195
|
+
`[host-cache-sweep] disk usage ${usePct}% >= ${highPercent}% threshold — emitting host.disk.high`,
|
|
196
|
+
);
|
|
197
|
+
recordEvent({
|
|
198
|
+
kind: "host.disk.high",
|
|
199
|
+
payload: { usePct, threshold: highPercent, df: dfOut, completedAt: new Date().toISOString() },
|
|
200
|
+
});
|
|
201
|
+
}
|
|
319
202
|
}
|
|
320
203
|
} catch (error) {
|
|
321
|
-
log.warn(
|
|
204
|
+
log.warn(`[host-cache-sweep] disk-threshold check failed: ${String(error)}`);
|
|
322
205
|
}
|
|
323
206
|
}
|
|
324
207
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @system server-boot
|
|
3
|
+
* @status handwritten
|
|
4
|
+
* @edit edit directly
|
|
5
|
+
*
|
|
6
|
+
* Pure transition-detection logic for the WhatsApp linked-device liveness monitor
|
|
7
|
+
* (`whatsapp-liveness` handler). Split out from the GOWA-coupled handler so the
|
|
8
|
+
* logic is unit-testable with zero adapter dependencies, and the handler is a thin
|
|
9
|
+
* poll→detect→emit shell. The connection signal is the GOWA device `state` field
|
|
10
|
+
* (`"logged_in"` == connected) — see `whatsapp-remote-logout-monitor`.
|
|
11
|
+
*/
|
|
12
|
+
/** GOWA device `state` value meaning the linked phone is connected + authenticated. */
|
|
13
|
+
export declare const GOWA_CONNECTED_STATE = "logged_in";
|
|
14
|
+
export interface WhatsAppDeviceState {
|
|
15
|
+
id: string;
|
|
16
|
+
state: string;
|
|
17
|
+
jid?: string;
|
|
18
|
+
display_name?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface LivenessTransitions {
|
|
21
|
+
disconnected: WhatsAppDeviceState[];
|
|
22
|
+
reconnected: WhatsAppDeviceState[];
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Detect connected↔disconnected transitions between the last-known and the freshly
|
|
26
|
+
* observed device states.
|
|
27
|
+
*
|
|
28
|
+
* `prev` is the last-known `state` per device id. A device ABSENT from `prev` is a
|
|
29
|
+
* first sighting: it seeds the baseline and yields NO transition (so the monitor
|
|
30
|
+
* never alerts on a state it never observed the device leave, and a process restart
|
|
31
|
+
* re-baselines without false positives). A device present in `prev` is compared:
|
|
32
|
+
* connected→not is `disconnected`; not→connected is `reconnected`.
|
|
33
|
+
*/
|
|
34
|
+
export declare function detectLivenessTransitions(prev: Map<string, string>, curr: WhatsAppDeviceState[]): LivenessTransitions;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @system service-boot
|
|
3
|
+
* @status handwritten
|
|
4
|
+
* @edit edit directly
|
|
5
|
+
*
|
|
6
|
+
* The single app.onError factory for every Hono app (UI + backend). Two error
|
|
7
|
+
* classes, deliberately separate:
|
|
8
|
+
* - HTTPException: a DELIBERATE status-bearing throw (e.g. hono bodyLimit's
|
|
9
|
+
* 413). Preserve its status + message verbatim — buildErrorReport only knows
|
|
10
|
+
* 500/503 and would erase a deliberate 413/401/etc.
|
|
11
|
+
* - anything else: an UNEXPECTED error → buildErrorReport (errorId correlation,
|
|
12
|
+
* transient-vs-bug classification, 503 + Retry-After when retryable, else 500).
|
|
13
|
+
*
|
|
14
|
+
* Resolves the apparent tension between `hono-global-error-handler` (HTTPException
|
|
15
|
+
* → status + JSON) and `error-responses-via-error-report` (every error response
|
|
16
|
+
* via buildErrorReport): the latter governs UNEXPECTED errors; HTTPException is
|
|
17
|
+
* the documented deliberate-status carve-out. The handler is a consumer of
|
|
18
|
+
* buildErrorReport, never a second classifier for the unexpected branch.
|
|
19
|
+
*/
|
|
20
|
+
import type { Context } from "hono";
|
|
21
|
+
/**
|
|
22
|
+
* Returns an `app.onError` handler tagged with `label` (used in the log line so
|
|
23
|
+
* the boot site that surfaced the error is identifiable).
|
|
24
|
+
*/
|
|
25
|
+
export declare function createRequestErrorHandler(label: string): (err: unknown, c: Context) => Response;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @system server-boot
|
|
3
|
+
* @status handwritten
|
|
4
|
+
* @edit edit directly
|
|
5
|
+
* @indivisible-unit
|
|
6
|
+
*
|
|
7
|
+
* Install harness — sandbox-mounts each declared package and captures
|
|
8
|
+
* registry.require() calls via AsyncLocalStorage. Validates all required
|
|
9
|
+
* rows exist before returning. Throws HarnessInstallError with full
|
|
10
|
+
* context on failure. Per Constitution Rule 2 step 3.
|
|
11
|
+
*
|
|
12
|
+
* Marked @indivisible-unit: HarnessInstallError is the error class
|
|
13
|
+
* validatePackageDependencies (might) throw — they form one canonical
|
|
14
|
+
* "install harness validation" unit (the validator + the failure-mode
|
|
15
|
+
* class it produces).
|
|
16
|
+
*/
|
|
17
|
+
export interface PackageDescriptor {
|
|
18
|
+
name: string;
|
|
19
|
+
initPath: string;
|
|
20
|
+
}
|
|
21
|
+
export interface HarnessValidationResult {
|
|
22
|
+
valid: true;
|
|
23
|
+
dependencies: Array<{
|
|
24
|
+
slug: string;
|
|
25
|
+
}>;
|
|
26
|
+
}
|
|
27
|
+
export interface HarnessValidationError {
|
|
28
|
+
valid: false;
|
|
29
|
+
missing: Array<{
|
|
30
|
+
slug: string;
|
|
31
|
+
packageName: string;
|
|
32
|
+
}>;
|
|
33
|
+
}
|
|
34
|
+
export type HarnessOutcome = HarnessValidationResult | HarnessValidationError;
|
|
35
|
+
export declare class HarnessInstallError extends Error {
|
|
36
|
+
readonly missing: Array<{
|
|
37
|
+
slug: string;
|
|
38
|
+
packageName: string;
|
|
39
|
+
}>;
|
|
40
|
+
constructor(missing: Array<{
|
|
41
|
+
slug: string;
|
|
42
|
+
packageName: string;
|
|
43
|
+
}>);
|
|
44
|
+
}
|
|
45
|
+
export declare function validatePackageDependencies(packages: PackageDescriptor[], mountFn: (pkg: PackageDescriptor) => Promise<void>): Promise<HarnessOutcome>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @system server-boot
|
|
3
|
+
* @status handwritten
|
|
4
|
+
* @edit edit directly
|
|
5
|
+
* @indivisible-unit
|
|
6
|
+
*
|
|
7
|
+
* One-call install of the entire logging pipeline. After installLogging()
|
|
8
|
+
* runs, every error in the service — process-level, HTTP middleware, Hono
|
|
9
|
+
* handlers, Puck blocks, even third-party packages calling console.* — is
|
|
10
|
+
* captured by @teamscala/logger and flows to Sentry/GlitchTip (prod) or
|
|
11
|
+
* loga (dev) with full context. Application code never imports the logger
|
|
12
|
+
* or wraps anything in try/catch for telemetry purposes.
|
|
13
|
+
*
|
|
14
|
+
* Marked @indivisible-unit: installLogging (early bootstrap, hardcoded
|
|
15
|
+
* defaults) and applyLoggerConfig (late-stage, DB-driven) are the two
|
|
16
|
+
* phases of the same one-call install — the service boot pipeline calls
|
|
17
|
+
* them in sequence with the same serviceSlug.
|
|
18
|
+
*/
|
|
19
|
+
import { attachRequestLogger } from "@teamscala/logger/install-logging";
|
|
20
|
+
export { attachRequestLogger };
|
|
21
|
+
export interface InstallLoggingOptions {
|
|
22
|
+
serviceSlug: string;
|
|
23
|
+
}
|
|
24
|
+
/** Initial bootstrap install — runs BEFORE DB/Registry are available.
|
|
25
|
+
* Uses hardcoded defaults (INFO level, /tmp/scala-logs). */
|
|
26
|
+
export declare function installLogging(opts: InstallLoggingOptions): void;
|
|
27
|
+
/** Late-stage configuration — runs AFTER Registry is loaded.
|
|
28
|
+
* Injects DB-driven settings into the logger primitive. */
|
|
29
|
+
export declare function applyLoggerConfig(config: {
|
|
30
|
+
minLevel?: "debug" | "info" | "warn" | "error";
|
|
31
|
+
logDir?: string;
|
|
32
|
+
maxFileSize?: number;
|
|
33
|
+
maxFiles?: number;
|
|
34
|
+
}): void;
|
package/src/install-logging.ts
CHANGED
|
@@ -19,7 +19,6 @@
|
|
|
19
19
|
|
|
20
20
|
import { ManagedCache } from "@teamscala/cache/cache";
|
|
21
21
|
import { createCache } from "@teamscala/cache/create-cache";
|
|
22
|
-
import { createNotifyListener, getDirectDatabaseUrl } from "@teamscala/db/notify-listener/index";
|
|
23
22
|
import { createDiskCache } from "@teamscala/cache/disk-cache";
|
|
24
23
|
import { join } from "node:path";
|
|
25
24
|
import { userHome } from "@teamscala/os/host-paths";
|
|
@@ -181,5 +180,3 @@ function safeStringify(v: unknown): string {
|
|
|
181
180
|
return String(v);
|
|
182
181
|
}
|
|
183
182
|
}
|
|
184
|
-
// Guard predicate: event-driven invalidation subscription
|
|
185
|
-
{ const _u = getDirectDatabaseUrl(); if (_u) createNotifyListener(_u).listen("registry_config_changed", () => {}).catch((e: unknown) => console.error("[cache-invalidation] subscribe failed: " + e)); }
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @system server-boot
|
|
3
|
+
* @status handwritten
|
|
4
|
+
* @edit edit directly
|
|
5
|
+
*
|
|
6
|
+
* Loads the service_install registry row for a given service slug.
|
|
7
|
+
* Uses registry.require() per Constitution Rule 11.
|
|
8
|
+
* Schema is owned by @teamscala/db-validation (Rule 7).
|
|
9
|
+
*/
|
|
10
|
+
import type { ServiceInstallConfig } from "@teamscala/db-validation/registry-schemas/service-install";
|
|
11
|
+
export { ServiceInstallConfigSchema } from "@teamscala/db-validation/registry-schemas/service-install";
|
|
12
|
+
export type { ServiceInstallConfig };
|
|
13
|
+
export interface LoadServiceInstallRowResult {
|
|
14
|
+
success: true;
|
|
15
|
+
config: ServiceInstallConfig;
|
|
16
|
+
}
|
|
17
|
+
export interface LoadServiceInstallRowError {
|
|
18
|
+
success: false;
|
|
19
|
+
error: "row_not_found" | "validation_failed";
|
|
20
|
+
slug: string;
|
|
21
|
+
message: string;
|
|
22
|
+
}
|
|
23
|
+
export type LoadServiceInstallRowOutcome = LoadServiceInstallRowResult | LoadServiceInstallRowError;
|
|
24
|
+
export declare function loadServiceInstallRow(serviceSlug: string): Promise<LoadServiceInstallRowOutcome>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @system server-boot
|
|
3
|
+
* @status handwritten
|
|
4
|
+
* @edit edit directly
|
|
5
|
+
*
|
|
6
|
+
* Loads the service's own generated PrismaClient from
|
|
7
|
+
* <cwd>/prisma/generated/prisma/client.ts. Each service repo generates its own
|
|
8
|
+
* client from its own schema.prisma; resolving a static import would pick up
|
|
9
|
+
* whichever @prisma/generated bun resolves first in a multi-service workspace,
|
|
10
|
+
* which is wrong.
|
|
11
|
+
*
|
|
12
|
+
* No fallback: if the generated client is missing the deploy is broken — fail
|
|
13
|
+
* loud with the remediation step.
|
|
14
|
+
*/
|
|
15
|
+
export type ServicePrismaClientConstructor = new (args: {
|
|
16
|
+
adapter: unknown;
|
|
17
|
+
}) => unknown;
|
|
18
|
+
export declare function loadServicePrismaClient(serviceSlug: string): Promise<ServicePrismaClientConstructor>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @system service-boot
|
|
3
|
+
* @status handwritten
|
|
4
|
+
* @edit edit directly
|
|
5
|
+
*
|
|
6
|
+
* Mounts the composed ORPC app router (generated Prisma CRUD + custom fn routers)
|
|
7
|
+
* at /api/rpc/* over HTTP via @orpc/server/fetch's RPCHandler.
|
|
8
|
+
* Uses getAppRouter() from @teamscala/orpc/app-router which merges generated
|
|
9
|
+
* routers with custom routers registered via registerCustomRouter().
|
|
10
|
+
*/
|
|
11
|
+
import type { Hono } from "hono";
|
|
12
|
+
export interface OrpcMountResult {
|
|
13
|
+
routeCount: number;
|
|
14
|
+
}
|
|
15
|
+
export declare function mountOrpcRouter(app: Hono, serviceSlug: string): Promise<OrpcMountResult | null>;
|
package/src/mount-orpc-router.ts
CHANGED
|
@@ -37,7 +37,7 @@ export async function mountOrpcRouter(
|
|
|
37
37
|
);
|
|
38
38
|
|
|
39
39
|
// No generated ORPC router is the EXPECTED state for daemon-only / boot-hook
|
|
40
|
-
// services (session-picker,
|
|
40
|
+
// services (session-picker, package-poller, etc.) that own no DB schema or
|
|
41
41
|
// api_routes — they serve no /api/rpc surface. Skip silently: importing a
|
|
42
42
|
// nonexistent module would throw into the catch below and surface a
|
|
43
43
|
// misleading "ORPC router not mounted" WARN on every boot. The catch is
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @system server-boot
|
|
3
|
+
* @status handwritten
|
|
4
|
+
* @edit edit directly
|
|
5
|
+
*
|
|
6
|
+
* Walks `service_install.server_packages[]`, dynamic-imports each, and calls
|
|
7
|
+
* its `mount(ctx)` function. Server packages register handlers / boot hooks /
|
|
8
|
+
* scheduled jobs / WS routes via the MountContext exposed in ctx; the
|
|
9
|
+
* resulting MountRegistry is consumed by register-api-routes /
|
|
10
|
+
* register-ws-routes / run-boot-hook-list / start-scheduled-jobs.
|
|
11
|
+
*/
|
|
12
|
+
import type { ServiceInstallConfig } from "@teamscala/db/registry/loader/load-service-install-row";
|
|
13
|
+
import { type MountRegistry } from "@teamscala/os/runtime-contracts/mount-context";
|
|
14
|
+
import type { Hono } from "hono";
|
|
15
|
+
/**
|
|
16
|
+
* Speculatively import every server_package's mount module so Bun compiles
|
|
17
|
+
* the module graphs into its cache NOW, overlapping the (CPU-bound)
|
|
18
|
+
* compilation with whatever I/O the caller runs next. When mountServerPackages
|
|
19
|
+
* later imports the same paths they hit the cache (instant).
|
|
20
|
+
*
|
|
21
|
+
* Same pattern as create-boot-pipeline's speculative preloads — the
|
|
22
|
+
* cross-internet DB I/O in the gate chain (~3.4s) is dead time the compiler
|
|
23
|
+
* can use. Best-effort + non-fatal: a failed import here (wrong candidate
|
|
24
|
+
* path, transient resolution race) is silently swallowed because
|
|
25
|
+
* mountServerPackages will re-attempt with full error surfacing; the preload
|
|
26
|
+
* only warms the cache, never gates boot.
|
|
27
|
+
*/
|
|
28
|
+
export declare function preloadServerPackages(packages: string[]): Promise<void>;
|
|
29
|
+
export declare function mountServerPackages(app: Hono, config: ServiceInstallConfig, serviceSlug: string): Promise<MountRegistry>;
|
package/src/mount.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @system server-boot
|
|
3
|
+
* @status handwritten
|
|
4
|
+
* @edit edit directly
|
|
5
|
+
*
|
|
6
|
+
* Server-package mount for opt-in service-boot watchdogs.
|
|
7
|
+
*/
|
|
8
|
+
import type { MountContext } from "@teamscala/os/runtime-contracts/mount-context";
|
|
9
|
+
export declare function mount(ctx: MountContext): void;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @system service-boot
|
|
3
|
+
* @status handwritten
|
|
4
|
+
* @edit edit directly
|
|
5
|
+
*
|
|
6
|
+
* Mutable app reference for two-phase boot. Allows swapping the Hono app
|
|
7
|
+
* atomically after building the full app in the background. The initial app
|
|
8
|
+
* is a minimal tool-only surface (ORPC, data routes) that starts serving
|
|
9
|
+
* immediately; the full app (pages, hydration) is built off-to-the-side and
|
|
10
|
+
* swapped in once complete.
|
|
11
|
+
*
|
|
12
|
+
* Thread-safety: Bun is single-threaded, so a simple module-level variable
|
|
13
|
+
* is sufficient. Atomics are not needed.
|
|
14
|
+
*/
|
|
15
|
+
import type { Hono } from "hono";
|
|
16
|
+
/**
|
|
17
|
+
* Create a new mutable app reference initialized with the provided app.
|
|
18
|
+
* This should be called once at boot time.
|
|
19
|
+
*/
|
|
20
|
+
export declare function initMutableAppRef(initialApp: Hono, label: string): void;
|
|
21
|
+
/**
|
|
22
|
+
* Get the current app. Throws if the ref hasn't been initialized.
|
|
23
|
+
*/
|
|
24
|
+
export declare function getApp(): Hono;
|
|
25
|
+
/**
|
|
26
|
+
* Get the current app label for logging.
|
|
27
|
+
*/
|
|
28
|
+
export declare function getAppLabel(): string;
|
|
29
|
+
/**
|
|
30
|
+
* Atomically swap the app to a new instance. This is called after the
|
|
31
|
+
* full app is built in the background. Returns the previous label for logging.
|
|
32
|
+
*/
|
|
33
|
+
export declare function swapApp(newApp: Hono, newLabel: string): string;
|
|
34
|
+
/**
|
|
35
|
+
* Fetch handler that routes through the mutable app reference.
|
|
36
|
+
* This replaces the direct app.fetch call in the serve layer.
|
|
37
|
+
*
|
|
38
|
+
* Stamps transport-derived identity (x-caller-remote-address/-port + the
|
|
39
|
+
* loopback local-caller comm) via the SAME stampTransportIdentity helper as
|
|
40
|
+
* direct mode, so two-phase-boot (mutable-ref) UI services carry identical
|
|
41
|
+
* identity on every request — closing the gap where the mutable-ref path
|
|
42
|
+
* previously served requests with no transport headers stamped at all.
|
|
43
|
+
*/
|
|
44
|
+
export declare function fetchWithMutableApp(req: Request, server: ReturnType<typeof Bun.serve>): Promise<Response>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @system ui-service-boot
|
|
3
|
+
* @status handwritten
|
|
4
|
+
* @edit edit directly
|
|
5
|
+
*
|
|
6
|
+
* Render mode literal shared across page route registration. The old
|
|
7
|
+
* shell-variant types (LegacyShell, PageSlugShell, RegionsShell,
|
|
8
|
+
* PublicShell, ConfigPageRoute) were removed when the Phase 2 deploy-time
|
|
9
|
+
* `service_install.page_routes[]` path was eliminated — all routing is
|
|
10
|
+
* now registry-driven `page_route` rows (Phase 1).
|
|
11
|
+
*/
|
|
12
|
+
export type RenderModeLiteral = "public" | "portals" | "utility";
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @system page-server
|
|
3
|
+
* @status handwritten
|
|
4
|
+
* @edit edit directly
|
|
5
|
+
*
|
|
6
|
+
* Audit revert procedure — re-applies an audit_trail row's changes_before to
|
|
7
|
+
* the entity via the canonical generated Prisma procedure for the entity's
|
|
8
|
+
* table: INSERT→delete the created row, UPDATE→restore the old values,
|
|
9
|
+
* DELETE→recreate from the deleted snapshot. The revert write itself fires the
|
|
10
|
+
* audit extension + DB trigger, recording a NEW forward audit_trail row —
|
|
11
|
+
* history is never edited (audit-trail-permanent; the undo is itself audited).
|
|
12
|
+
*
|
|
13
|
+
* Gated to super-admin (reverting business data is privileged). The caller
|
|
14
|
+
* must pass the audit_trail row id; the procedure resolves table + action +
|
|
15
|
+
* before-snapshot from that row.
|
|
16
|
+
*/
|
|
17
|
+
import * as v from "valibot";
|
|
18
|
+
export declare const auditRevertRouter: {
|
|
19
|
+
/**
|
|
20
|
+
* Revert a single audited change by re-applying changes_before through the
|
|
21
|
+
* generated Prisma model for the entity's table. Returns the action taken.
|
|
22
|
+
*/
|
|
23
|
+
revert: import("@orpc/server").DecoratedProcedure<Record<never, never>, Record<never, never>, v.ObjectSchema<{
|
|
24
|
+
readonly auditTrailId: v.StringSchema<undefined>;
|
|
25
|
+
}, undefined>, import("@orpc/contract").Schema<{
|
|
26
|
+
success: true;
|
|
27
|
+
action: string;
|
|
28
|
+
table: string;
|
|
29
|
+
entityId: string;
|
|
30
|
+
}, {
|
|
31
|
+
success: true;
|
|
32
|
+
action: string;
|
|
33
|
+
table: string;
|
|
34
|
+
entityId: string;
|
|
35
|
+
}>, Record<never, never>, Record<never, never>>;
|
|
36
|
+
};
|
|
37
|
+
export type AuditRevertRouter = typeof auditRevertRouter;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @system page-server
|
|
3
|
+
* @status handwritten
|
|
4
|
+
* @edit edit directly
|
|
5
|
+
*
|
|
6
|
+
* Module-level singleton for the hub MCP client. setHubMcpClient(client)
|
|
7
|
+
* is called once at boot by the host service after creating the multi-
|
|
8
|
+
* tenant MCP client; createPageHandler's resolve-page-state reads it via
|
|
9
|
+
* getHubMcpClient to pass through to data loaders.
|
|
10
|
+
*/
|
|
11
|
+
export declare function setHubMcpClient(client: unknown): void;
|
|
12
|
+
export declare function getHubMcpClient(): unknown | undefined;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @system page-server
|
|
3
|
+
* @status handwritten
|
|
4
|
+
* @edit edit directly
|
|
5
|
+
*
|
|
6
|
+
* ORPC procedures for portal diagnostics. These replace direct Prisma calls
|
|
7
|
+
* in the portal diagnostic section files with proper ORPC procedures that
|
|
8
|
+
* receive context.prisma from the router.
|
|
9
|
+
*/
|
|
10
|
+
import * as v from "valibot";
|
|
11
|
+
export declare const portalDiagRouter: {
|
|
12
|
+
findPortalWebsite: import("@orpc/server").DecoratedProcedure<Record<never, never>, Record<never, never>, v.ObjectSchema<{}, undefined>, v.NullableSchema<v.ObjectSchema<{
|
|
13
|
+
readonly id: v.StringSchema<undefined>;
|
|
14
|
+
readonly name: v.StringSchema<undefined>;
|
|
15
|
+
readonly organisation_id: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
16
|
+
}, undefined>, undefined>, Record<never, never>, Record<never, never>>;
|
|
17
|
+
findMember: import("@orpc/server").DecoratedProcedure<Record<never, never>, Record<never, never>, v.ObjectSchema<{
|
|
18
|
+
readonly organisationId: v.StringSchema<undefined>;
|
|
19
|
+
}, undefined>, v.NullableSchema<v.ObjectSchema<{
|
|
20
|
+
readonly user_id: v.StringSchema<undefined>;
|
|
21
|
+
}, undefined>, undefined>, Record<never, never>, Record<never, never>>;
|
|
22
|
+
countPages: import("@orpc/server").DecoratedProcedure<Record<never, never>, Record<never, never>, v.ObjectSchema<{
|
|
23
|
+
readonly websiteId: v.StringSchema<undefined>;
|
|
24
|
+
}, undefined>, v.NumberSchema<undefined>, Record<never, never>, Record<never, never>>;
|
|
25
|
+
findPages: import("@orpc/server").DecoratedProcedure<Record<never, never>, Record<never, never>, v.ObjectSchema<{
|
|
26
|
+
readonly websiteId: v.StringSchema<undefined>;
|
|
27
|
+
readonly path: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
28
|
+
readonly take: v.OptionalSchema<v.NumberSchema<undefined>, undefined>;
|
|
29
|
+
readonly orderBy: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
30
|
+
}, undefined>, v.ArraySchema<v.ObjectSchema<{
|
|
31
|
+
readonly id: v.StringSchema<undefined>;
|
|
32
|
+
readonly path: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
33
|
+
readonly name: v.StringSchema<undefined>;
|
|
34
|
+
readonly puck_data: v.UnknownSchema;
|
|
35
|
+
readonly updated_at: v.StringSchema<undefined>;
|
|
36
|
+
}, undefined>, undefined>, Record<never, never>, Record<never, never>>;
|
|
37
|
+
findBlockSchemas: import("@orpc/server").DecoratedProcedure<Record<never, never>, Record<never, never>, v.ObjectSchema<{
|
|
38
|
+
readonly slugs: v.ArraySchema<v.StringSchema<undefined>, undefined>;
|
|
39
|
+
}, undefined>, v.ArraySchema<v.ObjectSchema<{
|
|
40
|
+
readonly slug: v.StringSchema<undefined>;
|
|
41
|
+
}, undefined>, undefined>, Record<never, never>, Record<never, never>>;
|
|
42
|
+
findWebsites: import("@orpc/server").DecoratedProcedure<Record<never, never>, Record<never, never>, v.ObjectSchema<{
|
|
43
|
+
readonly take: v.OptionalSchema<v.NumberSchema<undefined>, undefined>;
|
|
44
|
+
}, undefined>, v.ArraySchema<v.ObjectSchema<{
|
|
45
|
+
readonly id: v.StringSchema<undefined>;
|
|
46
|
+
readonly name: v.StringSchema<undefined>;
|
|
47
|
+
}, undefined>, undefined>, Record<never, never>, Record<never, never>>;
|
|
48
|
+
};
|
|
49
|
+
export type PortalDiagRouter = typeof portalDiagRouter;
|