@shaferllc/keel 0.66.0 → 0.74.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 (239) hide show
  1. package/AGENTS.md +169 -0
  2. package/README.md +44 -6
  3. package/bin/keel-mcp.mjs +9 -0
  4. package/dist/api/api.config.stub +9 -0
  5. package/dist/api/config.d.ts +13 -0
  6. package/dist/api/config.js +14 -0
  7. package/dist/api/index.d.ts +16 -0
  8. package/dist/api/index.js +13 -0
  9. package/dist/api/provider.d.ts +10 -0
  10. package/dist/api/provider.js +17 -0
  11. package/dist/api/query.d.ts +35 -0
  12. package/dist/api/query.js +42 -0
  13. package/dist/api/resource.d.ts +91 -0
  14. package/dist/api/resource.js +188 -0
  15. package/dist/core/application.d.ts +5 -5
  16. package/dist/core/application.js +8 -2
  17. package/dist/core/cache.d.ts +81 -5
  18. package/dist/core/cache.js +188 -23
  19. package/dist/core/cli/stubs.d.ts +26 -0
  20. package/dist/core/cli/stubs.js +225 -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/events.d.ts +129 -5
  32. package/dist/core/events.js +165 -7
  33. package/dist/core/health.d.ts +141 -0
  34. package/dist/core/health.js +226 -0
  35. package/dist/core/helpers.d.ts +9 -3
  36. package/dist/core/helpers.js +11 -3
  37. package/dist/core/http/kernel.d.ts +2 -0
  38. package/dist/core/http/kernel.js +48 -0
  39. package/dist/core/http/router.d.ts +5 -5
  40. package/dist/core/http/router.js +5 -5
  41. package/dist/core/i18n.d.ts +162 -0
  42. package/dist/core/i18n.js +472 -0
  43. package/dist/core/index.d.ts +39 -13
  44. package/dist/core/index.js +19 -6
  45. package/dist/core/instrumentation.d.ts +113 -0
  46. package/dist/core/instrumentation.js +52 -0
  47. package/dist/core/lock.d.ts +139 -0
  48. package/dist/core/lock.js +215 -0
  49. package/dist/core/logger.d.ts +89 -4
  50. package/dist/core/logger.js +167 -22
  51. package/dist/core/mail.d.ts +128 -7
  52. package/dist/core/mail.js +264 -16
  53. package/dist/core/notification.js +10 -1
  54. package/dist/core/package.d.ts +120 -0
  55. package/dist/core/package.js +169 -0
  56. package/dist/core/pages.d.ts +108 -0
  57. package/dist/core/pages.js +199 -0
  58. package/dist/core/queue.d.ts +134 -9
  59. package/dist/core/queue.js +325 -14
  60. package/dist/core/repl.d.ts +33 -0
  61. package/dist/core/repl.js +88 -0
  62. package/dist/core/scheduler.js +6 -0
  63. package/dist/core/social.d.ts +4 -4
  64. package/dist/core/social.js +4 -4
  65. package/dist/core/storage.d.ts +159 -6
  66. package/dist/core/storage.js +299 -7
  67. package/dist/core/telemetry.d.ts +208 -0
  68. package/dist/core/telemetry.js +383 -0
  69. package/dist/core/template.d.ts +2 -3
  70. package/dist/core/template.js +2 -3
  71. package/dist/core/testing.d.ts +170 -1
  72. package/dist/core/testing.js +504 -2
  73. package/dist/db/d1.js +13 -0
  74. package/dist/db/pg.d.ts +13 -0
  75. package/dist/db/pg.js +46 -4
  76. package/dist/mcp/server.d.ts +19 -0
  77. package/dist/mcp/server.js +355 -0
  78. package/dist/openapi/config.d.ts +28 -0
  79. package/dist/openapi/config.js +25 -0
  80. package/dist/openapi/doc.d.ts +40 -0
  81. package/dist/openapi/doc.js +20 -0
  82. package/dist/openapi/export.d.ts +8 -0
  83. package/dist/openapi/export.js +19 -0
  84. package/dist/openapi/gate.d.ts +15 -0
  85. package/dist/openapi/gate.js +27 -0
  86. package/dist/openapi/index.d.ts +19 -0
  87. package/dist/openapi/index.js +15 -0
  88. package/dist/openapi/openapi.config.stub +29 -0
  89. package/dist/openapi/provider.d.ts +18 -0
  90. package/dist/openapi/provider.js +35 -0
  91. package/dist/openapi/routes.d.ts +9 -0
  92. package/dist/openapi/routes.js +23 -0
  93. package/dist/openapi/spec.d.ts +23 -0
  94. package/dist/openapi/spec.js +132 -0
  95. package/dist/openapi/ui.d.ts +8 -0
  96. package/dist/openapi/ui.js +31 -0
  97. package/dist/openapi/zod.d.ts +12 -0
  98. package/dist/openapi/zod.js +46 -0
  99. package/dist/watch/config.d.ts +33 -0
  100. package/dist/watch/config.js +38 -0
  101. package/dist/watch/entry.d.ts +53 -0
  102. package/dist/watch/entry.js +105 -0
  103. package/dist/watch/gate.d.ts +20 -0
  104. package/dist/watch/gate.js +32 -0
  105. package/dist/watch/index.d.ts +21 -0
  106. package/dist/watch/index.js +17 -0
  107. package/dist/watch/migration.d.ts +7 -0
  108. package/dist/watch/migration.js +20 -0
  109. package/dist/watch/provider.d.ts +22 -0
  110. package/dist/watch/provider.js +58 -0
  111. package/dist/watch/prune.d.ts +11 -0
  112. package/dist/watch/prune.js +20 -0
  113. package/dist/watch/recorder.d.ts +24 -0
  114. package/dist/watch/recorder.js +39 -0
  115. package/dist/watch/routes.d.ts +13 -0
  116. package/dist/watch/routes.js +55 -0
  117. package/dist/watch/store.d.ts +54 -0
  118. package/dist/watch/store.js +158 -0
  119. package/dist/watch/ui/dist/watch.css +1 -0
  120. package/dist/watch/ui/dist/watch.js +555 -0
  121. package/dist/watch/ui-shell.d.ts +12 -0
  122. package/dist/watch/ui-shell.js +24 -0
  123. package/dist/watch/watch.config.stub +47 -0
  124. package/dist/watch/watchers.d.ts +12 -0
  125. package/dist/watch/watchers.js +156 -0
  126. package/docs/ai-manifest.json +3206 -0
  127. package/docs/ai.md +128 -0
  128. package/docs/api-resources.md +118 -0
  129. package/docs/architecture.md +331 -0
  130. package/docs/authentication.md +453 -0
  131. package/docs/authorization.md +167 -0
  132. package/docs/broadcasting.md +137 -0
  133. package/docs/broker.md +500 -0
  134. package/docs/cache.md +558 -0
  135. package/docs/configuration.md +385 -0
  136. package/docs/console.md +536 -0
  137. package/docs/container.md +467 -0
  138. package/docs/controllers.md +265 -0
  139. package/docs/cors.md +51 -0
  140. package/docs/database.md +631 -0
  141. package/docs/debugging.md +129 -0
  142. package/docs/decorators.md +127 -0
  143. package/docs/errors.md +395 -0
  144. package/docs/events.md +496 -0
  145. package/docs/examples/architecture-app.ts +27 -0
  146. package/docs/examples/authentication.ts +61 -0
  147. package/docs/examples/authorization.ts +79 -0
  148. package/docs/examples/broadcasting.ts +60 -0
  149. package/docs/examples/broker-cache-validate.ts +34 -0
  150. package/docs/examples/broker-fault-tolerance.ts +29 -0
  151. package/docs/examples/broker-middleware.ts +27 -0
  152. package/docs/examples/broker.ts +203 -0
  153. package/docs/examples/cache.ts +222 -0
  154. package/docs/examples/configuration.ts +121 -0
  155. package/docs/examples/console.ts +134 -0
  156. package/docs/examples/container.ts +134 -0
  157. package/docs/examples/controllers.ts +86 -0
  158. package/docs/examples/database.ts +208 -0
  159. package/docs/examples/debugging.ts +41 -0
  160. package/docs/examples/decorators.ts +40 -0
  161. package/docs/examples/errors.ts +121 -0
  162. package/docs/examples/events.ts +204 -0
  163. package/docs/examples/factories.ts +84 -0
  164. package/docs/examples/hashing.ts +71 -0
  165. package/docs/examples/health.ts +94 -0
  166. package/docs/examples/helpers.ts +171 -0
  167. package/docs/examples/hooks.ts +54 -0
  168. package/docs/examples/i18n.ts +117 -0
  169. package/docs/examples/inertia.ts +81 -0
  170. package/docs/examples/locks.ts +120 -0
  171. package/docs/examples/logger.ts +166 -0
  172. package/docs/examples/mail.ts +263 -0
  173. package/docs/examples/middleware.ts +119 -0
  174. package/docs/examples/migrations.ts +126 -0
  175. package/docs/examples/models.ts +239 -0
  176. package/docs/examples/notification.ts +124 -0
  177. package/docs/examples/pages.ts +82 -0
  178. package/docs/examples/providers.ts +123 -0
  179. package/docs/examples/queues.ts +254 -0
  180. package/docs/examples/rate-limiting.ts +42 -0
  181. package/docs/examples/redis.ts +99 -0
  182. package/docs/examples/request-response.ts +197 -0
  183. package/docs/examples/routing.ts +186 -0
  184. package/docs/examples/scheduling.ts +62 -0
  185. package/docs/examples/sessions.ts +102 -0
  186. package/docs/examples/static-files.ts +63 -0
  187. package/docs/examples/storage.ts +132 -0
  188. package/docs/examples/telemetry.ts +127 -0
  189. package/docs/examples/templates.ts +58 -0
  190. package/docs/examples/testing.ts +215 -0
  191. package/docs/examples/transformer.ts +141 -0
  192. package/docs/examples/transformers.ts +49 -0
  193. package/docs/examples/url-builder.ts +86 -0
  194. package/docs/examples/validation.ts +102 -0
  195. package/docs/examples/views.tsx +62 -0
  196. package/docs/examples/vite.ts +106 -0
  197. package/docs/factories.md +166 -0
  198. package/docs/getting-started.md +290 -0
  199. package/docs/hashing.md +259 -0
  200. package/docs/health.md +225 -0
  201. package/docs/helpers.md +347 -0
  202. package/docs/hono.md +186 -0
  203. package/docs/hooks.md +118 -0
  204. package/docs/i18n.md +302 -0
  205. package/docs/inertia.md +241 -0
  206. package/docs/locks.md +323 -0
  207. package/docs/logger.md +436 -0
  208. package/docs/mail.md +751 -0
  209. package/docs/middleware.md +425 -0
  210. package/docs/migrations.md +476 -0
  211. package/docs/models.md +810 -0
  212. package/docs/notifications.md +474 -0
  213. package/docs/openapi.md +111 -0
  214. package/docs/packages.md +118 -0
  215. package/docs/pages.md +217 -0
  216. package/docs/providers.md +363 -0
  217. package/docs/queues.md +679 -0
  218. package/docs/rate-limiting.md +155 -0
  219. package/docs/redis.md +178 -0
  220. package/docs/request-response.md +953 -0
  221. package/docs/routing.md +804 -0
  222. package/docs/scheduling.md +110 -0
  223. package/docs/security.md +85 -0
  224. package/docs/sessions.md +354 -0
  225. package/docs/social-auth.md +174 -0
  226. package/docs/static-files.md +211 -0
  227. package/docs/storage.md +452 -0
  228. package/docs/telemetry.md +263 -0
  229. package/docs/templates.md +314 -0
  230. package/docs/testing.md +376 -0
  231. package/docs/transformers.md +381 -0
  232. package/docs/url-builder.md +295 -0
  233. package/docs/validation.md +288 -0
  234. package/docs/views.md +267 -0
  235. package/docs/vite.md +434 -0
  236. package/docs/watch.md +118 -0
  237. package/llms-full.txt +19816 -0
  238. package/llms.txt +127 -0
  239. package/package.json +42 -7
@@ -0,0 +1,171 @@
1
+ // Type-check harness for docs/helpers.md. Every type-checkable snippet in the
2
+ // guide is exercised here against the real exports, so a renamed helper or wrong
3
+ // argument type fails `npm run typecheck:docs`. Compile-only — never executed.
4
+ import {
5
+ app,
6
+ config,
7
+ view,
8
+ bind,
9
+ singleton,
10
+ instance,
11
+ make,
12
+ bound,
13
+ events,
14
+ emit,
15
+ listen,
16
+ cache,
17
+ logger,
18
+ Config,
19
+ type Listener,
20
+ type Token,
21
+ type Factory,
22
+ type Renderable,
23
+ } from "@shaferllc/keel/core";
24
+
25
+ // Stand-ins for app-defined values referenced in the guide.
26
+ type User = { id: number };
27
+ class Mailer {
28
+ constructor(_dep?: unknown) {}
29
+ }
30
+
31
+ declare const user: User;
32
+ declare const order: { id: number };
33
+ declare const code: string;
34
+ declare const orderId: number;
35
+ declare function computeStats(): Promise<{ total: number }>;
36
+ declare function sendWelcome(u: User): void;
37
+ declare function fulfil(id: number): void;
38
+ declare function Welcome(props: { appName: string }): Renderable;
39
+ declare function HomePage(): Renderable;
40
+
41
+ // --- Intro ---
42
+ export function intro() {
43
+ const name = config<string>("app.name", "Keel");
44
+ return name;
45
+ }
46
+
47
+ export async function introUsage() {
48
+ const stats = await cache().remember("stats", 60, () => computeStats());
49
+ await emit("user.registered", user);
50
+ logger().info("welcome sent", { userId: user.id });
51
+ return stats;
52
+ }
53
+
54
+ // --- Container helpers, up close ---
55
+ export function containerHelpers() {
56
+ singleton(Mailer, (a) => new Mailer(a.make(Config)));
57
+ const mailer = make(Mailer);
58
+ if (bound("clock")) {
59
+ /* someone registered it */
60
+ }
61
+ return mailer;
62
+ }
63
+
64
+ // --- Events, cache, logger ---
65
+ export async function servicesUpClose() {
66
+ listen("order.paid", (o: { id: number }) => fulfil(o.id));
67
+ await emit("order.paid", order);
68
+ events().listenerCount("order.paid");
69
+
70
+ await cache().put("otp", code, 300);
71
+ logger().warn("retrying", { attempt: 2 });
72
+ }
73
+
74
+ // --- Rendering a view ---
75
+ function WelcomeInline({ appName }: { appName: string }): Renderable {
76
+ return `<h1>Welcome to ${appName}</h1>`;
77
+ }
78
+
79
+ export async function rendering() {
80
+ const a = await view(WelcomeInline, { appName: "Keel" });
81
+ const b = await view(HomePage);
82
+ return { a, b };
83
+ }
84
+
85
+ // --- API reference: app() ---
86
+ export function appHelper() {
87
+ const port = app().config().get<number>("app.port", 3000);
88
+ return port;
89
+ }
90
+
91
+ // --- config() ---
92
+ export function configHelper() {
93
+ const a = config<string>("app.name");
94
+ const b = config("app.port", 3000);
95
+ return { a, b };
96
+ }
97
+
98
+ // --- view() ---
99
+ export async function viewHelper() {
100
+ const a = await view(Welcome, { appName: "Keel" });
101
+ const b = await view(HomePage);
102
+ return { a, b };
103
+ }
104
+
105
+ // --- bind() ---
106
+ export function bindHelper() {
107
+ bind("clock", () => new Date());
108
+ }
109
+
110
+ // --- singleton() ---
111
+ export function singletonHelper() {
112
+ singleton(Mailer, (a) => new Mailer(a.make(Config)));
113
+ }
114
+
115
+ // --- instance() ---
116
+ export function instanceHelper() {
117
+ const version = instance("app.version", "0.30.0");
118
+ return version;
119
+ }
120
+
121
+ // --- make() ---
122
+ export function makeHelper() {
123
+ const mailer = make(Mailer);
124
+ const version = make<string>("app.version");
125
+ return { mailer, version };
126
+ }
127
+
128
+ // --- bound() ---
129
+ export function boundHelper() {
130
+ if (bound("clock")) return make<Date>("clock");
131
+ return new Date();
132
+ }
133
+
134
+ // --- events() ---
135
+ export function eventsHelper() {
136
+ events().listenerCount("order.paid");
137
+ events().clear("order.paid");
138
+ }
139
+
140
+ // --- emit() ---
141
+ export async function emitHelper() {
142
+ await emit("user.registered", user);
143
+ }
144
+
145
+ // --- listen() ---
146
+ export function listenHelper() {
147
+ const off = listen("user.registered", (u: User) => sendWelcome(u));
148
+ off();
149
+ }
150
+
151
+ // --- cache() ---
152
+ export async function cacheHelper() {
153
+ const stats = await cache().remember("stats", 60, () => computeStats());
154
+ await cache().put("otp", code, 300);
155
+ return stats;
156
+ }
157
+
158
+ // --- logger() ---
159
+ export function loggerHelper() {
160
+ logger().info("user registered", { userId: user.id });
161
+ logger().error("payment failed", { orderId });
162
+ }
163
+
164
+ // --- Interfaces & types ---
165
+ const onOrder: Listener<{ id: number }> = async (o) => fulfil(o.id);
166
+ const strToken: Token<string> = "app.version";
167
+ const svcToken: Token<Mailer> = Mailer;
168
+ const mailerFactory: Factory<Mailer> = (a) => new Mailer(a.make(Config));
169
+ const renderable: Renderable = "<p>hi</p>";
170
+
171
+ export { onOrder, strToken, svcToken, mailerFactory, renderable };
@@ -0,0 +1,54 @@
1
+ // Type-check harness for docs/hooks.md. Compile-only — never executed.
2
+ import {
3
+ Application,
4
+ Router,
5
+ onReady,
6
+ onShutdown,
7
+ terminate,
8
+ logger,
9
+ redis,
10
+ type LifecycleHook,
11
+ } from "@shaferllc/keel/core";
12
+
13
+ declare function warmCaches(): Promise<void>;
14
+
15
+ export function globalHooks() {
16
+ onReady(async () => {
17
+ await warmCaches();
18
+ logger().info("app ready");
19
+ });
20
+ onShutdown(async () => {
21
+ await redis().flushAll();
22
+ });
23
+ }
24
+
25
+ export function signals() {
26
+ for (const signal of ["SIGINT", "SIGTERM"] as const) {
27
+ process.on(signal, async () => {
28
+ await terminate();
29
+ process.exit(0);
30
+ });
31
+ }
32
+ }
33
+
34
+ export async function methodForm() {
35
+ const app = new Application();
36
+ app
37
+ .onReady((a) => void a)
38
+ .onShutdown(async () => {});
39
+ await app.boot([], { discoverConfig: false, config: { app: {} } });
40
+ const done: boolean = app.isTerminated;
41
+ await app.terminate();
42
+ return done;
43
+ }
44
+
45
+ export function routeHook(app: Application) {
46
+ const router = app.make(Router);
47
+ router.onRoute((def) => {
48
+ logger().debug("route", { methods: def.methods, path: def.path, name: def.name });
49
+ });
50
+ }
51
+
52
+ // The hook type
53
+ const ready: LifecycleHook = (app) => void app;
54
+ export { ready };
@@ -0,0 +1,117 @@
1
+ // Type-check harness for docs/i18n.md. Compile-only — never executed.
2
+ import {
3
+ i18n,
4
+ t,
5
+ setI18n,
6
+ getI18n,
7
+ setTranslations,
8
+ detectLocale,
9
+ negotiateLocale,
10
+ formatMessage,
11
+ objectLoader,
12
+ I18n,
13
+ I18nManager,
14
+ HttpKernel,
15
+ ServiceProvider,
16
+ listen,
17
+ logger,
18
+ type TranslationsByLocale,
19
+ type TranslationLoader,
20
+ type I18nOptions,
21
+ type DetectLocaleOptions,
22
+ } from "@shaferllc/keel/core";
23
+
24
+ declare const order: { createdAt: Date };
25
+ declare const post: { publishedAt: Date };
26
+ declare const names: string[];
27
+
28
+ export class I18nServiceProvider extends ServiceProvider {
29
+ boot(): void {
30
+ setI18n(new I18nManager({ defaultLocale: "en" }));
31
+
32
+ setTranslations({
33
+ en: { "cart.items": "{count, plural, one {# item} other {# items}}" },
34
+ fr: { "cart.items": "{count, plural, one {# article} other {# articles}}" },
35
+ });
36
+
37
+ this.app.make(HttpKernel).use(detectLocale());
38
+ }
39
+ }
40
+
41
+ export function translating() {
42
+ return [t("cart.items", { count: 3 }), i18n("fr").t("cart.items", { count: 3 })];
43
+ }
44
+
45
+ export function formatters() {
46
+ const l: I18n = i18n();
47
+ return {
48
+ number: l.formatNumber(1234.5),
49
+ currency: l.formatCurrency(9.5, "USD"),
50
+ date: l.formatDate(order.createdAt),
51
+ time: l.formatTime(order.createdAt),
52
+ relative: l.formatRelativeTime(post.publishedAt),
53
+ relativeUnit: l.formatRelativeTime(post.publishedAt, "hour"),
54
+ list: l.formatList(["a", "b", "c"]),
55
+ or: l.formatList(names, { type: "disjunction" }),
56
+ plural: l.formatPlural(5),
57
+ display: l.formatDisplayName("fr"),
58
+ has: l.has("cart.items"),
59
+ locale: l.locale,
60
+ };
61
+ }
62
+
63
+ export function detection(): DetectLocaleOptions[] {
64
+ return [{}, { query: "lang" }, { cookie: "locale" }, { header: false }, { resolve: () => "fr" }];
65
+ }
66
+
67
+ export function middleware() {
68
+ return [detectLocale(), detectLocale({ query: "lang", cookie: "locale" })];
69
+ }
70
+
71
+ export function negotiating() {
72
+ return negotiateLocale("fr-CA,fr;q=0.9,en;q=0.8", ["en", "fr"], "en");
73
+ }
74
+
75
+ export function messages() {
76
+ return [
77
+ formatMessage("Hello {name}!", { name: "Ada" }),
78
+ formatMessage("{count, plural, =0 {Empty} one {# item} other {# items}}", { count: 3 }, "en"),
79
+ formatMessage("{n, selectordinal, one {#st} other {#th}}", { n: 1 }, "en"),
80
+ formatMessage("{gender, select, male {He} other {They}}", { gender: "male" }),
81
+ formatMessage("{n, number, ::currency/USD}", { n: 9.5 }, "en-US"),
82
+ ];
83
+ }
84
+
85
+ export function fallbacks() {
86
+ setTranslations({
87
+ es: { greeting: "Hola", chair: "silla" },
88
+ "es-MX": { chair: "banca" },
89
+ });
90
+
91
+ return [i18n("es-MX").t("chair"), i18n("es-MX").t("greeting")];
92
+ }
93
+
94
+ export function missingKeys() {
95
+ listen("i18n.missing", (payload) => {
96
+ const { key, locale } = payload as { key: string; locale: string };
97
+ logger().warn("missing translation", { key, locale });
98
+ });
99
+
100
+ const options: I18nOptions = {
101
+ defaultLocale: "en",
102
+ supportedLocales: ["en", "fr"],
103
+ fallbackLocales: { "es-MX": "es" },
104
+ missing: (key, locale) => `[${locale}:${key}]`,
105
+ };
106
+ return new I18nManager(options);
107
+ }
108
+
109
+ export async function loaders() {
110
+ const data: TranslationsByLocale = { en: { hi: "Hi" } };
111
+ const loader: TranslationLoader = objectLoader(data);
112
+
113
+ const manager = new I18nManager();
114
+ await manager.load(loader);
115
+
116
+ return { manager, supported: manager.supported(), active: getI18n().defaultLocale };
117
+ }
@@ -0,0 +1,81 @@
1
+ // Type-check harness for docs/inertia.md. Every type-checkable snippet in the
2
+ // reference is exercised here against the real exports, so a renamed method or
3
+ // wrong argument type fails `npm run typecheck:docs`. Compile-only — never run.
4
+ import {
5
+ ServiceProvider,
6
+ singleton,
7
+ Inertia,
8
+ inertia,
9
+ inertiaPageAttr,
10
+ type InertiaPage,
11
+ type InertiaOptions,
12
+ } from "@shaferllc/keel/core";
13
+
14
+ declare const user: { id: number; name: string };
15
+ declare const stats: unknown;
16
+ declare const notifications: unknown;
17
+ declare const activity: unknown;
18
+ declare const page: InertiaPage;
19
+ declare const rootView: (page: InertiaPage) => string;
20
+
21
+ // Configure it
22
+ export class InertiaServiceProvider extends ServiceProvider {
23
+ register(): void {
24
+ singleton(
25
+ Inertia,
26
+ () =>
27
+ new Inertia({
28
+ version: "1",
29
+ rootView: (page) =>
30
+ `<!DOCTYPE html><html><head><meta charset="utf-8"></head>` +
31
+ `<body><div id="app" data-page="${inertiaPageAttr(page)}"></div>` +
32
+ `<script src="/assets/app.js"></script></body></html>`,
33
+ }),
34
+ );
35
+ }
36
+ }
37
+
38
+ // Render a page
39
+ export function render() {
40
+ const a: Response | string = inertia("Users/Show", { user });
41
+ const b: Response | string = inertia("Dashboard");
42
+ return { a, b };
43
+ }
44
+
45
+ // Partial reload example
46
+ export function partial() {
47
+ return inertia("Dashboard", { stats, notifications, activity });
48
+ }
49
+
50
+ // Reference: inertiaPageAttr
51
+ export function attr() {
52
+ return `<div id="app" data-page="${inertiaPageAttr(page)}"></div>`;
53
+ }
54
+
55
+ // Reference: Inertia class
56
+ export function classUsage() {
57
+ const i = new Inertia({
58
+ version: "1",
59
+ rootView: (page) => `<div id="app" data-page="${inertiaPageAttr(page)}"></div>`,
60
+ });
61
+ const html: Response | string = i.render("Dashboard", { title: "Welcome" });
62
+ const noProps: Response | string = i.render("Dashboard");
63
+ return { html, noProps };
64
+ }
65
+
66
+ // Interface / type seams
67
+ const options: InertiaOptions = {
68
+ version: "abc123",
69
+ rootView: (page) => `<div id="app" data-page="${inertiaPageAttr(page)}"></div>`,
70
+ };
71
+
72
+ const pageSeam: InertiaPage = {
73
+ component: "Users/Show",
74
+ props: { user },
75
+ url: "/users/1",
76
+ version: "1",
77
+ };
78
+
79
+ const _rootView = rootView;
80
+
81
+ export { options, pageSeam, _rootView };
@@ -0,0 +1,120 @@
1
+ // Type-check harness for docs/locks.md. Compile-only — never executed.
2
+ import {
3
+ lock,
4
+ restoreLock,
5
+ setLockStore,
6
+ getLockStore,
7
+ Lock,
8
+ MemoryLockStore,
9
+ LockNotHeldError,
10
+ type LockStore,
11
+ type AcquireOptions,
12
+ } from "@shaferllc/keel/core";
13
+
14
+ declare const invoice: { id: number };
15
+ declare function charge(invoice: { id: number }): Promise<void>;
16
+ declare function rebuild(): Promise<void>;
17
+ declare function process(batch: unknown): Promise<void>;
18
+ declare const batches: unknown[];
19
+
20
+ export async function running() {
21
+ const [ran, result] = await lock("invoice:42").run(() => charge(invoice));
22
+ if (!ran) return undefined;
23
+ return result;
24
+ }
25
+
26
+ export async function waiting() {
27
+ const options: AcquireOptions = { timeout: 5_000, retryDelay: 100 };
28
+ const [ran] = await lock("report").run(() => rebuild(), options);
29
+
30
+ const [immediate] = await lock("report").runImmediately(() => rebuild());
31
+ return { ran, immediate };
32
+ }
33
+
34
+ export async function ttlAndExtend() {
35
+ const l = lock("import", 60_000);
36
+ await l.run(async () => {
37
+ for (const batch of batches) {
38
+ await process(batch);
39
+ await l.extend();
40
+ }
41
+ });
42
+ }
43
+
44
+ export async function manualAcquisition() {
45
+ const l = lock("invoice:42");
46
+ if (!(await l.acquire({ timeout: 2_000 }))) return;
47
+
48
+ try {
49
+ await charge(invoice);
50
+ } finally {
51
+ await l.release();
52
+ }
53
+ }
54
+
55
+ export async function handoff(): Promise<string> {
56
+ const l = lock("import:99", 60_000);
57
+ await l.acquire();
58
+ return l.serialize();
59
+ }
60
+
61
+ export async function resume(serialized: string) {
62
+ const l = restoreLock(serialized);
63
+ await l.extend(60_000);
64
+ await l.release();
65
+ }
66
+
67
+ export async function inspecting() {
68
+ const l = lock("k");
69
+ return {
70
+ locked: await l.isLocked(),
71
+ expired: await l.isExpired(),
72
+ remaining: await l.getRemainingTime(),
73
+ };
74
+ }
75
+
76
+ export function testing() {
77
+ setLockStore(new MemoryLockStore());
78
+ return getLockStore();
79
+ }
80
+
81
+ export function errors(err: unknown) {
82
+ return err instanceof LockNotHeldError;
83
+ }
84
+
85
+ // A custom store (the shape of the Redis / database recipes).
86
+ export function customStore(): LockStore {
87
+ const entries = new Map<string, { owner: string; expiresAt: number }>();
88
+ return {
89
+ async acquire(key, owner, ttlMs) {
90
+ const existing = entries.get(key);
91
+ if (existing && existing.expiresAt > Date.now()) return false;
92
+ entries.set(key, { owner, expiresAt: Date.now() + ttlMs });
93
+ return true;
94
+ },
95
+ async release(key, owner) {
96
+ const entry = entries.get(key);
97
+ if (!entry || entry.owner !== owner) return false;
98
+ entries.delete(key);
99
+ return true;
100
+ },
101
+ async extend(key, owner, ttlMs) {
102
+ const entry = entries.get(key);
103
+ if (!entry || entry.owner !== owner) return false;
104
+ entry.expiresAt = Date.now() + ttlMs;
105
+ return true;
106
+ },
107
+ async isLocked(key) {
108
+ const entry = entries.get(key);
109
+ return !!entry && entry.expiresAt > Date.now();
110
+ },
111
+ async remainingTime(key) {
112
+ const entry = entries.get(key);
113
+ return entry ? entry.expiresAt - Date.now() : null;
114
+ },
115
+ };
116
+ }
117
+
118
+ export function direct(): Lock {
119
+ return new Lock("k", 1_000, new MemoryLockStore());
120
+ }
@@ -0,0 +1,166 @@
1
+ // Type-check harness for docs/logger.md. Every type-checkable snippet in the
2
+ // reference is exercised here against the real exports, so a renamed method or
3
+ // wrong argument type fails `npm run typecheck:docs`. Compile-only — never run.
4
+ import {
5
+ logger,
6
+ Logger,
7
+ requestLogger,
8
+ requestLog,
9
+ HttpKernel,
10
+ type LoggerOptions,
11
+ type LogLevel,
12
+ } from "@shaferllc/keel/core";
13
+
14
+ declare const user: { id: number };
15
+ declare const key: string;
16
+ declare const orderId: number;
17
+ declare const err: unknown;
18
+ declare const sql: string;
19
+ declare const ms: number;
20
+ declare const requestId: string;
21
+ declare const jobId: string;
22
+
23
+ export function logging() {
24
+ logger().info("user registered", { userId: user.id });
25
+ logger().warn("cache miss", { key });
26
+ logger().error("payment failed", { orderId, error: String(err) });
27
+ logger().debug("query", { sql, ms });
28
+ }
29
+
30
+ export function levels() {
31
+ const threshold: LogLevel = "warn";
32
+ new Logger({ level: threshold });
33
+ }
34
+
35
+ export function childLoggers() {
36
+ const log = logger().child({ requestId });
37
+ log.info("handling");
38
+ log.info("done");
39
+
40
+ const base = logger().child({ service: "billing" });
41
+ const job = base.child({ jobId });
42
+ job.info("start");
43
+ }
44
+
45
+ export function constructing() {
46
+ const log = new Logger({ level: "debug", pretty: true, bindings: { env: "dev" } });
47
+ log.debug("boot", { pid: 1 });
48
+
49
+ const bare = new Logger();
50
+ bare.info("no options");
51
+ }
52
+
53
+ export function reference() {
54
+ logger().info("ready");
55
+
56
+ const log = new Logger({ level: "warn", pretty: true });
57
+ log.debug("cache lookup", { key });
58
+ log.info("user registered", { userId: 42 });
59
+ log.warn("cache miss", { key });
60
+ log.error("payment failed", { orderId, error: String(err) });
61
+
62
+ const reqLog = logger().child({ requestId });
63
+ reqLog.info("handling");
64
+ }
65
+
66
+ export function redaction() {
67
+ const log = new Logger({ redact: ["password", "req.headers.authorization"] });
68
+ log.info("login", {
69
+ user: "ada",
70
+ password: "s3cret",
71
+ req: { headers: { authorization: "Bearer x" } },
72
+ });
73
+ }
74
+
75
+ export function perRequest(kernel: HttpKernel) {
76
+ kernel.use(requestLogger());
77
+ kernel.use(requestLogger({ idHeader: "x-request-id", logRequests: false }));
78
+ }
79
+
80
+ export function inRequest() {
81
+ requestLog().info("charging card", { orderId: 1 });
82
+ }
83
+
84
+ // Interface / type seams
85
+ const options: LoggerOptions = {
86
+ level: "debug",
87
+ pretty: true,
88
+ bindings: { app: "api" },
89
+ redact: ["password"],
90
+ };
91
+ const level: LogLevel = "warn";
92
+ export { options, level };
93
+
94
+ /* --- Levels, sinks, named loggers, redaction options --- */
95
+
96
+ import {
97
+ MemorySink,
98
+ consoleSink,
99
+ setLogger,
100
+ namedLogger,
101
+ type Sink,
102
+ type LogRecord,
103
+ type RedactOptions,
104
+ } from "@shaferllc/keel/core";
105
+
106
+ declare function expensiveSnapshot(): unknown;
107
+ declare const auditSink: Sink;
108
+ declare const userId: number;
109
+
110
+ export function allLevels() {
111
+ const log = new Logger({ level: "trace" });
112
+ log.trace("t");
113
+ log.debug("d");
114
+ log.info("i");
115
+ log.warn("w");
116
+ log.error("e");
117
+ log.fatal("f");
118
+ log.log("warn", "dynamic");
119
+ }
120
+
121
+ export function gating() {
122
+ if (logger().isLevelEnabled("debug")) {
123
+ logger().debug("state", { snapshot: expensiveSnapshot() });
124
+ }
125
+ logger().ifLevelEnabled("debug", (log) =>
126
+ log.debug("state", { snapshot: expensiveSnapshot() }),
127
+ );
128
+ }
129
+
130
+ export function sinks() {
131
+ const httpSink: Sink = (record: LogRecord) => {
132
+ void fetch("https://logs.example.com", { method: "POST", body: JSON.stringify(record) });
133
+ };
134
+ new Logger({ sink: httpSink });
135
+ new Logger({ sink: consoleSink(true) });
136
+ new Logger({ enabled: false });
137
+ }
138
+
139
+ export function memorySink() {
140
+ const sink = new MemorySink();
141
+ const log = new Logger({ level: "trace", sink: sink.sink });
142
+
143
+ log.info("hello", { userId: 1 });
144
+
145
+ const messages = sink.messages();
146
+ const infos = sink.at("info");
147
+ const fields = sink.records[0]?.fields;
148
+ sink.clear();
149
+
150
+ return { messages, infos, fields };
151
+ }
152
+
153
+ export function named() {
154
+ setLogger(new Logger({ level: "trace", sink: auditSink }), "audit");
155
+ namedLogger("audit").trace("permission granted", { userId });
156
+ }
157
+
158
+ export function redactionOptions() {
159
+ new Logger({ redact: ["password", "req.headers.authorization"] });
160
+ new Logger({ redact: ["*.password", "creds.*.token"] });
161
+
162
+ const censor: RedactOptions = { paths: ["password"], censor: "***" };
163
+ const remove: RedactOptions = { paths: ["password"], remove: true };
164
+ new Logger({ redact: censor });
165
+ new Logger({ redact: remove });
166
+ }