@shaferllc/keel 0.68.0 → 0.77.0
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/AGENTS.md +2 -0
- package/README.md +15 -5
- package/dist/api/api.config.stub +9 -0
- package/dist/api/config.d.ts +13 -0
- package/dist/api/config.js +14 -0
- package/dist/api/index.d.ts +16 -0
- package/dist/api/index.js +13 -0
- package/dist/api/provider.d.ts +10 -0
- package/dist/api/provider.js +17 -0
- package/dist/api/query.d.ts +35 -0
- package/dist/api/query.js +42 -0
- package/dist/api/resource.d.ts +93 -0
- package/dist/api/resource.js +185 -0
- package/dist/core/application.js +6 -0
- package/dist/core/cache.d.ts +1 -2
- package/dist/core/cache.js +9 -2
- package/dist/core/cli/index.d.ts +28 -0
- package/dist/core/cli/index.js +427 -0
- package/dist/core/cli/stubs.d.ts +14 -0
- package/dist/core/cli/stubs.js +105 -0
- package/dist/core/console-prompt.d.ts +79 -0
- package/dist/core/console-prompt.js +239 -0
- package/dist/core/console-ui.d.ts +96 -0
- package/dist/core/console-ui.js +187 -0
- package/dist/core/console.d.ts +188 -0
- package/dist/core/console.js +395 -0
- package/dist/core/database.d.ts +70 -1
- package/dist/core/database.js +174 -15
- package/dist/core/env.d.ts +96 -0
- package/dist/core/env.js +140 -0
- package/dist/core/health.d.ts +2 -2
- package/dist/core/health.js +2 -2
- package/dist/core/http/kernel.d.ts +2 -0
- package/dist/core/http/kernel.js +48 -0
- package/dist/core/http/router.d.ts +5 -5
- package/dist/core/http/router.js +5 -5
- package/dist/core/i18n.d.ts +162 -0
- package/dist/core/i18n.js +472 -0
- package/dist/core/index.d.ts +25 -4
- package/dist/core/index.js +13 -3
- package/dist/core/instrumentation.d.ts +113 -0
- package/dist/core/instrumentation.js +52 -0
- package/dist/core/logger.d.ts +7 -0
- package/dist/core/logger.js +28 -1
- package/dist/core/notification.js +10 -1
- package/dist/core/package.d.ts +117 -0
- package/dist/core/package.js +169 -0
- package/dist/core/pages.d.ts +108 -0
- package/dist/core/pages.js +199 -0
- package/dist/core/queue.js +26 -5
- package/dist/core/repl.d.ts +33 -0
- package/dist/core/repl.js +88 -0
- package/dist/core/scheduler.js +6 -0
- package/dist/core/social.d.ts +4 -4
- package/dist/core/social.js +4 -4
- package/dist/core/storage.js +15 -3
- package/dist/core/telemetry.d.ts +208 -0
- package/dist/core/telemetry.js +383 -0
- package/dist/core/template.d.ts +2 -3
- package/dist/core/template.js +2 -3
- package/dist/core/testing.d.ts +171 -1
- package/dist/core/testing.js +505 -2
- package/dist/db/d1.js +13 -0
- package/dist/db/pg.d.ts +13 -0
- package/dist/db/pg.js +46 -4
- package/dist/openapi/config.d.ts +28 -0
- package/dist/openapi/config.js +25 -0
- package/dist/openapi/doc.d.ts +40 -0
- package/dist/openapi/doc.js +20 -0
- package/dist/openapi/export.d.ts +8 -0
- package/dist/openapi/export.js +19 -0
- package/dist/openapi/gate.d.ts +15 -0
- package/dist/openapi/gate.js +27 -0
- package/dist/openapi/index.d.ts +19 -0
- package/dist/openapi/index.js +15 -0
- package/dist/openapi/openapi.config.stub +29 -0
- package/dist/openapi/provider.d.ts +18 -0
- package/dist/openapi/provider.js +35 -0
- package/dist/openapi/routes.d.ts +9 -0
- package/dist/openapi/routes.js +23 -0
- package/dist/openapi/spec.d.ts +23 -0
- package/dist/openapi/spec.js +132 -0
- package/dist/openapi/ui.d.ts +8 -0
- package/dist/openapi/ui.js +31 -0
- package/dist/openapi/zod.d.ts +12 -0
- package/dist/openapi/zod.js +46 -0
- package/dist/watch/config.d.ts +33 -0
- package/dist/watch/config.js +38 -0
- package/dist/watch/entry.d.ts +53 -0
- package/dist/watch/entry.js +105 -0
- package/dist/watch/gate.d.ts +20 -0
- package/dist/watch/gate.js +32 -0
- package/dist/watch/index.d.ts +21 -0
- package/dist/watch/index.js +17 -0
- package/dist/watch/migration.d.ts +7 -0
- package/dist/watch/migration.js +20 -0
- package/dist/watch/provider.d.ts +22 -0
- package/dist/watch/provider.js +58 -0
- package/dist/watch/prune.d.ts +11 -0
- package/dist/watch/prune.js +22 -0
- package/dist/watch/recorder.d.ts +24 -0
- package/dist/watch/recorder.js +39 -0
- package/dist/watch/routes.d.ts +13 -0
- package/dist/watch/routes.js +55 -0
- package/dist/watch/store.d.ts +54 -0
- package/dist/watch/store.js +158 -0
- package/dist/watch/ui/dist/watch.css +1 -0
- package/dist/watch/ui/dist/watch.js +555 -0
- package/dist/watch/ui-shell.d.ts +12 -0
- package/dist/watch/ui-shell.js +24 -0
- package/dist/watch/watch.config.stub +47 -0
- package/dist/watch/watchers.d.ts +12 -0
- package/dist/watch/watchers.js +156 -0
- package/docs/ai-manifest.json +737 -3
- package/docs/api-resources.md +119 -0
- package/docs/configuration.md +74 -0
- package/docs/console.md +217 -13
- package/docs/database.md +101 -0
- package/docs/examples/api-resources.ts +86 -0
- package/docs/examples/configuration.ts +40 -0
- package/docs/examples/console.ts +134 -0
- package/docs/examples/database.ts +90 -0
- package/docs/examples/i18n.ts +117 -0
- package/docs/examples/logger.ts +74 -0
- package/docs/examples/mail.ts +103 -0
- package/docs/examples/pages.ts +82 -0
- package/docs/examples/telemetry.ts +127 -0
- package/docs/examples/testing.ts +150 -0
- package/docs/health.md +4 -4
- package/docs/i18n.md +302 -0
- package/docs/logger.md +156 -10
- package/docs/mail.md +73 -0
- package/docs/openapi.md +111 -0
- package/docs/packages.md +118 -0
- package/docs/pages.md +217 -0
- package/docs/storage.md +5 -3
- package/docs/telemetry.md +263 -0
- package/docs/templates.md +3 -4
- package/docs/testing.md +252 -0
- package/docs/watch.md +118 -0
- package/llms-full.txt +2389 -241
- package/llms.txt +13 -1
- package/package.json +32 -6
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The watchers. Each subscribes to one instrumentation seam and turns it into a
|
|
3
|
+
* recorded entry — the whole reason the framework emits those events. None of
|
|
4
|
+
* them patch anything: they're just listeners, so installing Watch changes no
|
|
5
|
+
* behaviour, only observation.
|
|
6
|
+
*
|
|
7
|
+
* `installWatchers` wires up every enabled watcher and hands back a single
|
|
8
|
+
* teardown function (used on shutdown and in tests).
|
|
9
|
+
*/
|
|
10
|
+
import { listen, events } from "../core/helpers.js";
|
|
11
|
+
import { tapLogs } from "../core/logger.js";
|
|
12
|
+
import { familyHash, redactHeaders, sqlShape } from "./entry.js";
|
|
13
|
+
/** Events the Event watcher ignores — the instrumentation stream and things with
|
|
14
|
+
* their own dedicated watcher, so they aren't recorded twice. */
|
|
15
|
+
const OWN_EVENTS = new Set([
|
|
16
|
+
"db.query",
|
|
17
|
+
"request.handled",
|
|
18
|
+
"exception",
|
|
19
|
+
"job.processing",
|
|
20
|
+
"job.processed",
|
|
21
|
+
"job.failed",
|
|
22
|
+
"cache.hit",
|
|
23
|
+
"cache.miss",
|
|
24
|
+
"notification.sent",
|
|
25
|
+
"schedule.task.run",
|
|
26
|
+
"mail.sending",
|
|
27
|
+
"mail.sent",
|
|
28
|
+
"mail.queued",
|
|
29
|
+
]);
|
|
30
|
+
export function installWatchers(recorder, config) {
|
|
31
|
+
const off = [];
|
|
32
|
+
const dash = "/" + config.path.replace(/^\/|\/$/g, "");
|
|
33
|
+
const ignorePath = (path) => path === dash ||
|
|
34
|
+
path.startsWith(`${dash}/`) ||
|
|
35
|
+
config.ignorePaths.some((p) => path.startsWith(p));
|
|
36
|
+
/* ------------------------------- requests ------------------------------- */
|
|
37
|
+
if (recorder.enabledFor("request")) {
|
|
38
|
+
off.push(listen("request.handled", (e) => {
|
|
39
|
+
if (ignorePath(e.path))
|
|
40
|
+
return;
|
|
41
|
+
recorder.record("request", {
|
|
42
|
+
method: e.method,
|
|
43
|
+
path: e.path,
|
|
44
|
+
status: e.status,
|
|
45
|
+
durationMs: e.durationMs,
|
|
46
|
+
headers: redactHeaders(e.headers),
|
|
47
|
+
...(e.ip ? { ip: e.ip } : {}),
|
|
48
|
+
}, [
|
|
49
|
+
`status:${e.status}`,
|
|
50
|
+
`method:${e.method}`,
|
|
51
|
+
e.status >= 500 ? "failed" : e.status >= 400 ? "client-error" : "ok",
|
|
52
|
+
], { batchId: e.id, familyHash: familyHash(`${e.method} ${e.path}`) });
|
|
53
|
+
}));
|
|
54
|
+
}
|
|
55
|
+
/* -------------------------------- queries ------------------------------- */
|
|
56
|
+
if (recorder.enabledFor("query")) {
|
|
57
|
+
off.push(listen("db.query", (e) => {
|
|
58
|
+
// Never record the store's own reads/writes — that would be a loop.
|
|
59
|
+
if (e.sql.includes(config.table))
|
|
60
|
+
return;
|
|
61
|
+
const slow = e.durationMs >= config.slowQueryMs;
|
|
62
|
+
recorder.record("query", {
|
|
63
|
+
sql: e.sql,
|
|
64
|
+
bindings: e.bindings,
|
|
65
|
+
durationMs: e.durationMs,
|
|
66
|
+
connection: e.connection,
|
|
67
|
+
kind: e.kind,
|
|
68
|
+
}, [`connection:${e.connection}`, e.kind, ...(slow ? ["slow"] : [])], {
|
|
69
|
+
...(e.requestId ? { batchId: e.requestId } : {}),
|
|
70
|
+
familyHash: familyHash(sqlShape(e.sql)),
|
|
71
|
+
});
|
|
72
|
+
}));
|
|
73
|
+
}
|
|
74
|
+
/* ------------------------------ exceptions ------------------------------ */
|
|
75
|
+
if (recorder.enabledFor("exception")) {
|
|
76
|
+
off.push(listen("exception", (e) => {
|
|
77
|
+
const err = e.error;
|
|
78
|
+
const cls = err instanceof Error ? err.name : "Error";
|
|
79
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
80
|
+
recorder.record("exception", {
|
|
81
|
+
class: cls,
|
|
82
|
+
message,
|
|
83
|
+
status: e.status,
|
|
84
|
+
...(e.method ? { method: e.method } : {}),
|
|
85
|
+
...(e.path ? { path: e.path } : {}),
|
|
86
|
+
stack: err instanceof Error ? (err.stack ?? "").split("\n").map((l) => l.trim()) : [],
|
|
87
|
+
}, [`status:${e.status}`, cls], {
|
|
88
|
+
...(e.requestId ? { batchId: e.requestId } : {}),
|
|
89
|
+
familyHash: familyHash(`${cls}:${message}`),
|
|
90
|
+
});
|
|
91
|
+
}));
|
|
92
|
+
}
|
|
93
|
+
/* --------------------------------- logs --------------------------------- */
|
|
94
|
+
if (recorder.enabledFor("log")) {
|
|
95
|
+
off.push(tapLogs((record) => {
|
|
96
|
+
recorder.record("log", { level: record.level, message: record.msg, time: record.time, context: record.fields }, [`level:${record.level}`]);
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
/* --------------------------------- mail --------------------------------- */
|
|
100
|
+
if (recorder.enabledFor("mail")) {
|
|
101
|
+
off.push(listen("mail.sent", (msg) => {
|
|
102
|
+
const m = msg ?? {};
|
|
103
|
+
recorder.record("mail", {
|
|
104
|
+
to: m.to,
|
|
105
|
+
from: m.from,
|
|
106
|
+
...(m.cc ? { cc: m.cc } : {}),
|
|
107
|
+
...(m.bcc ? { bcc: m.bcc } : {}),
|
|
108
|
+
subject: m.subject,
|
|
109
|
+
...(typeof m.text === "string" ? { text: m.text } : {}),
|
|
110
|
+
...(typeof m.html === "string" ? { html: m.html } : {}),
|
|
111
|
+
}, ["mail:sent"]);
|
|
112
|
+
}));
|
|
113
|
+
}
|
|
114
|
+
/* --------------------------------- jobs --------------------------------- */
|
|
115
|
+
if (recorder.enabledFor("job")) {
|
|
116
|
+
off.push(listen("job.processed", (e) => {
|
|
117
|
+
recorder.record("job", { job: e.job, status: "processed", ...(e.durationMs != null ? { durationMs: e.durationMs } : {}) }, ["job:processed", e.job], e.requestId ? { batchId: e.requestId } : {});
|
|
118
|
+
}));
|
|
119
|
+
off.push(listen("job.failed", (e) => {
|
|
120
|
+
recorder.record("job", { job: e.job, status: "failed", error: e.error }, ["job:failed", e.job], e.requestId ? { batchId: e.requestId } : {});
|
|
121
|
+
}));
|
|
122
|
+
}
|
|
123
|
+
/* ----------------------------- notifications ---------------------------- */
|
|
124
|
+
if (recorder.enabledFor("notification")) {
|
|
125
|
+
off.push(listen("notification.sent", (e) => {
|
|
126
|
+
recorder.record("notification", { notification: e.notification, channels: e.channels, notifiable: e.notifiable }, ["notification:sent", ...e.channels.map((c) => `channel:${c}`)], e.requestId ? { batchId: e.requestId } : {});
|
|
127
|
+
}));
|
|
128
|
+
}
|
|
129
|
+
/* -------------------------------- cache --------------------------------- */
|
|
130
|
+
if (recorder.enabledFor("cache")) {
|
|
131
|
+
const cacheEntry = (hit) => (e) => recorder.record("cache", { key: e.key, hit, store: e.store }, [hit ? "cache:hit" : "cache:miss"], e.requestId ? { batchId: e.requestId } : {});
|
|
132
|
+
off.push(listen("cache.hit", cacheEntry(true)));
|
|
133
|
+
off.push(listen("cache.miss", cacheEntry(false)));
|
|
134
|
+
}
|
|
135
|
+
/* -------------------------------- events -------------------------------- */
|
|
136
|
+
if (recorder.enabledFor("event")) {
|
|
137
|
+
off.push(events().onAny((name, payload) => {
|
|
138
|
+
if (OWN_EVENTS.has(name))
|
|
139
|
+
return;
|
|
140
|
+
recorder.record("event", { name, payload }, [`event:${name}`]);
|
|
141
|
+
}));
|
|
142
|
+
}
|
|
143
|
+
/* ------------------------------- schedule ------------------------------- */
|
|
144
|
+
if (recorder.enabledFor("schedule")) {
|
|
145
|
+
off.push(listen("schedule.task.run", (e) => {
|
|
146
|
+
recorder.record("schedule", { task: e.task, durationMs: e.durationMs }, [
|
|
147
|
+
"schedule:run",
|
|
148
|
+
e.task,
|
|
149
|
+
]);
|
|
150
|
+
}));
|
|
151
|
+
}
|
|
152
|
+
return () => {
|
|
153
|
+
for (const unsub of off)
|
|
154
|
+
unsub();
|
|
155
|
+
};
|
|
156
|
+
}
|