@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.
Files changed (143) hide show
  1. package/AGENTS.md +2 -0
  2. package/README.md +15 -5
  3. package/dist/api/api.config.stub +9 -0
  4. package/dist/api/config.d.ts +13 -0
  5. package/dist/api/config.js +14 -0
  6. package/dist/api/index.d.ts +16 -0
  7. package/dist/api/index.js +13 -0
  8. package/dist/api/provider.d.ts +10 -0
  9. package/dist/api/provider.js +17 -0
  10. package/dist/api/query.d.ts +35 -0
  11. package/dist/api/query.js +42 -0
  12. package/dist/api/resource.d.ts +93 -0
  13. package/dist/api/resource.js +185 -0
  14. package/dist/core/application.js +6 -0
  15. package/dist/core/cache.d.ts +1 -2
  16. package/dist/core/cache.js +9 -2
  17. package/dist/core/cli/index.d.ts +28 -0
  18. package/dist/core/cli/index.js +427 -0
  19. package/dist/core/cli/stubs.d.ts +14 -0
  20. package/dist/core/cli/stubs.js +105 -0
  21. package/dist/core/console-prompt.d.ts +79 -0
  22. package/dist/core/console-prompt.js +239 -0
  23. package/dist/core/console-ui.d.ts +96 -0
  24. package/dist/core/console-ui.js +187 -0
  25. package/dist/core/console.d.ts +188 -0
  26. package/dist/core/console.js +395 -0
  27. package/dist/core/database.d.ts +70 -1
  28. package/dist/core/database.js +174 -15
  29. package/dist/core/env.d.ts +96 -0
  30. package/dist/core/env.js +140 -0
  31. package/dist/core/health.d.ts +2 -2
  32. package/dist/core/health.js +2 -2
  33. package/dist/core/http/kernel.d.ts +2 -0
  34. package/dist/core/http/kernel.js +48 -0
  35. package/dist/core/http/router.d.ts +5 -5
  36. package/dist/core/http/router.js +5 -5
  37. package/dist/core/i18n.d.ts +162 -0
  38. package/dist/core/i18n.js +472 -0
  39. package/dist/core/index.d.ts +25 -4
  40. package/dist/core/index.js +13 -3
  41. package/dist/core/instrumentation.d.ts +113 -0
  42. package/dist/core/instrumentation.js +52 -0
  43. package/dist/core/logger.d.ts +7 -0
  44. package/dist/core/logger.js +28 -1
  45. package/dist/core/notification.js +10 -1
  46. package/dist/core/package.d.ts +117 -0
  47. package/dist/core/package.js +169 -0
  48. package/dist/core/pages.d.ts +108 -0
  49. package/dist/core/pages.js +199 -0
  50. package/dist/core/queue.js +26 -5
  51. package/dist/core/repl.d.ts +33 -0
  52. package/dist/core/repl.js +88 -0
  53. package/dist/core/scheduler.js +6 -0
  54. package/dist/core/social.d.ts +4 -4
  55. package/dist/core/social.js +4 -4
  56. package/dist/core/storage.js +15 -3
  57. package/dist/core/telemetry.d.ts +208 -0
  58. package/dist/core/telemetry.js +383 -0
  59. package/dist/core/template.d.ts +2 -3
  60. package/dist/core/template.js +2 -3
  61. package/dist/core/testing.d.ts +171 -1
  62. package/dist/core/testing.js +505 -2
  63. package/dist/db/d1.js +13 -0
  64. package/dist/db/pg.d.ts +13 -0
  65. package/dist/db/pg.js +46 -4
  66. package/dist/openapi/config.d.ts +28 -0
  67. package/dist/openapi/config.js +25 -0
  68. package/dist/openapi/doc.d.ts +40 -0
  69. package/dist/openapi/doc.js +20 -0
  70. package/dist/openapi/export.d.ts +8 -0
  71. package/dist/openapi/export.js +19 -0
  72. package/dist/openapi/gate.d.ts +15 -0
  73. package/dist/openapi/gate.js +27 -0
  74. package/dist/openapi/index.d.ts +19 -0
  75. package/dist/openapi/index.js +15 -0
  76. package/dist/openapi/openapi.config.stub +29 -0
  77. package/dist/openapi/provider.d.ts +18 -0
  78. package/dist/openapi/provider.js +35 -0
  79. package/dist/openapi/routes.d.ts +9 -0
  80. package/dist/openapi/routes.js +23 -0
  81. package/dist/openapi/spec.d.ts +23 -0
  82. package/dist/openapi/spec.js +132 -0
  83. package/dist/openapi/ui.d.ts +8 -0
  84. package/dist/openapi/ui.js +31 -0
  85. package/dist/openapi/zod.d.ts +12 -0
  86. package/dist/openapi/zod.js +46 -0
  87. package/dist/watch/config.d.ts +33 -0
  88. package/dist/watch/config.js +38 -0
  89. package/dist/watch/entry.d.ts +53 -0
  90. package/dist/watch/entry.js +105 -0
  91. package/dist/watch/gate.d.ts +20 -0
  92. package/dist/watch/gate.js +32 -0
  93. package/dist/watch/index.d.ts +21 -0
  94. package/dist/watch/index.js +17 -0
  95. package/dist/watch/migration.d.ts +7 -0
  96. package/dist/watch/migration.js +20 -0
  97. package/dist/watch/provider.d.ts +22 -0
  98. package/dist/watch/provider.js +58 -0
  99. package/dist/watch/prune.d.ts +11 -0
  100. package/dist/watch/prune.js +22 -0
  101. package/dist/watch/recorder.d.ts +24 -0
  102. package/dist/watch/recorder.js +39 -0
  103. package/dist/watch/routes.d.ts +13 -0
  104. package/dist/watch/routes.js +55 -0
  105. package/dist/watch/store.d.ts +54 -0
  106. package/dist/watch/store.js +158 -0
  107. package/dist/watch/ui/dist/watch.css +1 -0
  108. package/dist/watch/ui/dist/watch.js +555 -0
  109. package/dist/watch/ui-shell.d.ts +12 -0
  110. package/dist/watch/ui-shell.js +24 -0
  111. package/dist/watch/watch.config.stub +47 -0
  112. package/dist/watch/watchers.d.ts +12 -0
  113. package/dist/watch/watchers.js +156 -0
  114. package/docs/ai-manifest.json +737 -3
  115. package/docs/api-resources.md +119 -0
  116. package/docs/configuration.md +74 -0
  117. package/docs/console.md +217 -13
  118. package/docs/database.md +101 -0
  119. package/docs/examples/api-resources.ts +86 -0
  120. package/docs/examples/configuration.ts +40 -0
  121. package/docs/examples/console.ts +134 -0
  122. package/docs/examples/database.ts +90 -0
  123. package/docs/examples/i18n.ts +117 -0
  124. package/docs/examples/logger.ts +74 -0
  125. package/docs/examples/mail.ts +103 -0
  126. package/docs/examples/pages.ts +82 -0
  127. package/docs/examples/telemetry.ts +127 -0
  128. package/docs/examples/testing.ts +150 -0
  129. package/docs/health.md +4 -4
  130. package/docs/i18n.md +302 -0
  131. package/docs/logger.md +156 -10
  132. package/docs/mail.md +73 -0
  133. package/docs/openapi.md +111 -0
  134. package/docs/packages.md +118 -0
  135. package/docs/pages.md +217 -0
  136. package/docs/storage.md +5 -3
  137. package/docs/telemetry.md +263 -0
  138. package/docs/templates.md +3 -4
  139. package/docs/testing.md +252 -0
  140. package/docs/watch.md +118 -0
  141. package/llms-full.txt +2389 -241
  142. package/llms.txt +13 -1
  143. package/package.json +32 -6
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Where recorded entries live. Two implementations behind one interface:
3
+ *
4
+ * - `MemoryStore` — a per-process ring buffer. Zero setup, great for a single
5
+ * dev process or the edge; entries vanish on restart.
6
+ * - `DatabaseStore` — a `watch_entries` table via any registered connection.
7
+ * Survives restarts and is shared across processes; needs `keel migrate`.
8
+ *
9
+ * The store's own reads and writes hit the database like anything else, so they
10
+ * would show up in the Query watcher — an infinite hall of mirrors. The watcher
11
+ * filters them out by table name (see `watchers.ts`); the store just does its job.
12
+ */
13
+ import { connection } from "../core/database.js";
14
+ import { ENTRY_TYPES } from "./entry.js";
15
+ /* -------------------------------- memory ---------------------------------- */
16
+ export class MemoryStore {
17
+ cap;
18
+ entries = [];
19
+ constructor(cap = 1000) {
20
+ this.cap = cap;
21
+ }
22
+ async record(entries) {
23
+ // newest first; trim the oldest past the cap
24
+ this.entries.unshift(...entries);
25
+ if (this.entries.length > this.cap)
26
+ this.entries.length = this.cap;
27
+ }
28
+ async get(uuid) {
29
+ return this.entries.find((e) => e.uuid === uuid);
30
+ }
31
+ async list(filter) {
32
+ let rows = this.entries;
33
+ if (filter.type)
34
+ rows = rows.filter((e) => e.type === filter.type);
35
+ if (filter.tag)
36
+ rows = rows.filter((e) => e.tags.includes(filter.tag));
37
+ if (filter.batchId)
38
+ rows = rows.filter((e) => e.batchId === filter.batchId);
39
+ if (filter.before != null)
40
+ rows = rows.filter((e) => e.createdAt < filter.before);
41
+ return rows.slice(0, filter.limit ?? 100);
42
+ }
43
+ async batch(batchId) {
44
+ return this.entries.filter((e) => e.batchId === batchId);
45
+ }
46
+ async counts() {
47
+ const counts = emptyCounts();
48
+ for (const e of this.entries)
49
+ counts[e.type]++;
50
+ return counts;
51
+ }
52
+ async prune(timestamp) {
53
+ const before = this.entries.length;
54
+ this.entries = this.entries.filter((e) => e.createdAt >= timestamp);
55
+ return before - this.entries.length;
56
+ }
57
+ async clear() {
58
+ this.entries = [];
59
+ }
60
+ }
61
+ export class DatabaseStore {
62
+ table;
63
+ connectionName;
64
+ constructor(table, connectionName) {
65
+ this.table = table;
66
+ this.connectionName = connectionName;
67
+ }
68
+ conn() {
69
+ return connection(this.connectionName);
70
+ }
71
+ async record(entries) {
72
+ const conn = this.conn();
73
+ for (const e of entries) {
74
+ await conn.write(`INSERT INTO ${this.table} (uuid, batch_id, type, family_hash, content, tags, created_at)
75
+ VALUES (?, ?, ?, ?, ?, ?, ?)`, [
76
+ e.uuid,
77
+ e.batchId,
78
+ e.type,
79
+ e.familyHash ?? null,
80
+ JSON.stringify(e.content),
81
+ // tags stored comma-wrapped (",a,b,") so `LIKE '%,tag,%'` matches exactly
82
+ e.tags.length ? `,${e.tags.join(",")},` : "",
83
+ e.createdAt,
84
+ ]);
85
+ }
86
+ }
87
+ async get(uuid) {
88
+ const rows = (await this.conn().select(`SELECT * FROM ${this.table} WHERE uuid = ? LIMIT 1`, [uuid]));
89
+ return rows[0] ? hydrate(rows[0]) : undefined;
90
+ }
91
+ async list(filter) {
92
+ const where = [];
93
+ const bindings = [];
94
+ if (filter.type) {
95
+ where.push("type = ?");
96
+ bindings.push(filter.type);
97
+ }
98
+ if (filter.batchId) {
99
+ where.push("batch_id = ?");
100
+ bindings.push(filter.batchId);
101
+ }
102
+ if (filter.tag) {
103
+ where.push("tags LIKE ?");
104
+ bindings.push(`%,${filter.tag},%`);
105
+ }
106
+ if (filter.before != null) {
107
+ where.push("created_at < ?");
108
+ bindings.push(filter.before);
109
+ }
110
+ const clause = where.length ? `WHERE ${where.join(" AND ")}` : "";
111
+ const rows = (await this.conn().select(`SELECT * FROM ${this.table} ${clause} ORDER BY created_at DESC LIMIT ${Number(filter.limit ?? 100)}`, bindings));
112
+ return rows.map(hydrate);
113
+ }
114
+ async batch(batchId) {
115
+ const rows = (await this.conn().select(`SELECT * FROM ${this.table} WHERE batch_id = ? ORDER BY created_at ASC`, [batchId]));
116
+ return rows.map(hydrate);
117
+ }
118
+ async counts() {
119
+ const rows = (await this.conn().select(`SELECT type, COUNT(*) AS n FROM ${this.table} GROUP BY type`, []));
120
+ const counts = emptyCounts();
121
+ for (const r of rows) {
122
+ if (r.type in counts)
123
+ counts[r.type] = Number(r.n);
124
+ }
125
+ return counts;
126
+ }
127
+ async prune(timestamp) {
128
+ const result = await this.conn().write(`DELETE FROM ${this.table} WHERE created_at < ?`, [timestamp]);
129
+ return result.rowsAffected;
130
+ }
131
+ async clear() {
132
+ await this.conn().write(`DELETE FROM ${this.table}`, []);
133
+ }
134
+ }
135
+ /* -------------------------------- helpers --------------------------------- */
136
+ function emptyCounts() {
137
+ return Object.fromEntries(ENTRY_TYPES.map((t) => [t, 0]));
138
+ }
139
+ /** Turn a stored row back into an Entry. */
140
+ function hydrate(row) {
141
+ return {
142
+ uuid: row.uuid,
143
+ batchId: row.batch_id,
144
+ type: row.type,
145
+ ...(row.family_hash ? { familyHash: row.family_hash } : {}),
146
+ content: safeParse(row.content),
147
+ tags: row.tags ? row.tags.split(",").filter(Boolean) : [],
148
+ createdAt: Number(row.created_at),
149
+ };
150
+ }
151
+ function safeParse(json) {
152
+ try {
153
+ return JSON.parse(json);
154
+ }
155
+ catch {
156
+ return { raw: json };
157
+ }
158
+ }
@@ -0,0 +1 @@
1
+ :root{--bg: #f7f8fa;--panel: #ffffff;--panel-2: #f0f2f5;--border: #e2e6ec;--text: #1e2430;--muted: #6b7686;--accent: #2563eb;--accent-soft: #dbe6ff;--ok: #16a34a;--warn: #d97706;--err: #dc2626;--chip: #eef1f6;--mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace}@media(prefers-color-scheme:dark){:root{--bg: #0b0f17;--panel: #121824;--panel-2: #0e131d;--border: #222b3a;--text: #e6eaf1;--muted: #8a94a6;--accent: #5b8dff;--accent-soft: #1b2740;--ok: #34d399;--warn: #fbbf24;--err: #f87171;--chip: #1a2333}}*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--text);font:14px/1.5 system-ui,-apple-system,Segoe UI,Roboto,sans-serif}.app{display:grid;grid-template-columns:232px 1fr;min-height:100vh}.sidebar{background:var(--panel);border-right:1px solid var(--border);display:flex;flex-direction:column;padding:16px 12px;gap:4px;position:sticky;top:0;height:100vh}.brand{font-weight:700;font-size:16px;padding:4px 8px 14px;letter-spacing:.2px}.brand .anchor{margin-right:4px}.sidebar nav{display:flex;flex-direction:column;gap:2px;flex:1}.tab{display:flex;align-items:center;justify-content:space-between;border:0;background:transparent;color:var(--muted);padding:8px 10px;border-radius:8px;cursor:pointer;font-size:13.5px;text-align:left}.tab:hover{background:var(--panel-2);color:var(--text)}.tab.active{background:var(--accent-soft);color:var(--accent);font-weight:600}.count{font-variant-numeric:tabular-nums;font-size:12px;background:var(--chip);color:var(--muted);border-radius:999px;padding:1px 8px;min-width:22px;text-align:center}.tab.active .count{background:var(--accent);color:#fff}.controls{display:flex;align-items:center;justify-content:space-between;padding:12px 8px 2px;border-top:1px solid var(--border);margin-top:8px}.live{display:flex;align-items:center;gap:6px;color:var(--muted);font-size:13px;cursor:pointer}.clear{border:1px solid var(--border);background:var(--panel-2);color:var(--muted);border-radius:7px;padding:5px 12px;cursor:pointer;font-size:12.5px}.clear:hover{color:var(--err);border-color:var(--err)}.content{padding:22px 26px;min-width:0}.head{display:flex;align-items:center;gap:12px;margin-bottom:16px}.head h1{font-size:18px;margin:0;font-weight:650;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.back,.chip.removable{border:1px solid var(--border);background:var(--panel);color:var(--muted);border-radius:7px;padding:5px 11px;cursor:pointer;font-size:13px}.back:hover{color:var(--accent);border-color:var(--accent)}.empty{color:var(--muted);padding:40px 4px}.list{width:100%;border-collapse:collapse;background:var(--panel);border:1px solid var(--border);border-radius:10px;overflow:hidden}.list tr{border-bottom:1px solid var(--border);cursor:pointer}.list tr:last-child{border-bottom:0}.list tr:hover{background:var(--panel-2)}.list td{padding:10px 14px;vertical-align:middle}.summary{display:flex;flex-direction:column;gap:5px;min-width:0}.summary .text{font-family:var(--mono);font-size:12.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:62vw}.tags{display:flex;gap:5px;flex-wrap:wrap}.chip{background:var(--chip);color:var(--muted);border-radius:999px;padding:1px 9px;font-size:11px;cursor:pointer;white-space:nowrap}.chip:hover{color:var(--accent)}.metacol,.timecol{text-align:right;color:var(--muted);font-size:12px;font-variant-numeric:tabular-nums;white-space:nowrap}.badge{color:var(--accent);font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;white-space:nowrap;width:1%}.detailtags{margin-bottom:14px}.json{background:var(--panel-2);border:1px solid var(--border);border-radius:10px;padding:16px;font-family:var(--mono);font-size:12.5px;line-height:1.6;overflow-x:auto;white-space:pre-wrap;word-break:break-word}.related{margin-top:24px}.related h2{font-size:13px;text-transform:uppercase;letter-spacing:.5px;color:var(--muted);margin:0 0 10px}@media(max-width:720px){.app{grid-template-columns:1fr}.sidebar{position:static;height:auto;flex-direction:row;flex-wrap:wrap;align-items:center}.sidebar nav{flex-direction:row;flex-wrap:wrap}.summary .text{max-width:70vw}}