@shaferllc/keel 0.68.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 (140) hide show
  1. package/AGENTS.md +2 -0
  2. package/README.md +14 -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 +91 -0
  13. package/dist/api/resource.js +188 -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/stubs.d.ts +14 -0
  18. package/dist/core/cli/stubs.js +105 -0
  19. package/dist/core/console-prompt.d.ts +79 -0
  20. package/dist/core/console-prompt.js +239 -0
  21. package/dist/core/console-ui.d.ts +96 -0
  22. package/dist/core/console-ui.js +187 -0
  23. package/dist/core/console.d.ts +188 -0
  24. package/dist/core/console.js +395 -0
  25. package/dist/core/database.d.ts +70 -1
  26. package/dist/core/database.js +174 -15
  27. package/dist/core/env.d.ts +96 -0
  28. package/dist/core/env.js +140 -0
  29. package/dist/core/health.d.ts +2 -2
  30. package/dist/core/health.js +2 -2
  31. package/dist/core/http/kernel.d.ts +2 -0
  32. package/dist/core/http/kernel.js +48 -0
  33. package/dist/core/http/router.d.ts +5 -5
  34. package/dist/core/http/router.js +5 -5
  35. package/dist/core/i18n.d.ts +162 -0
  36. package/dist/core/i18n.js +472 -0
  37. package/dist/core/index.d.ts +25 -4
  38. package/dist/core/index.js +13 -3
  39. package/dist/core/instrumentation.d.ts +113 -0
  40. package/dist/core/instrumentation.js +52 -0
  41. package/dist/core/logger.d.ts +7 -0
  42. package/dist/core/logger.js +28 -1
  43. package/dist/core/notification.js +10 -1
  44. package/dist/core/package.d.ts +120 -0
  45. package/dist/core/package.js +169 -0
  46. package/dist/core/pages.d.ts +108 -0
  47. package/dist/core/pages.js +199 -0
  48. package/dist/core/queue.js +26 -5
  49. package/dist/core/repl.d.ts +33 -0
  50. package/dist/core/repl.js +88 -0
  51. package/dist/core/scheduler.js +6 -0
  52. package/dist/core/social.d.ts +4 -4
  53. package/dist/core/social.js +4 -4
  54. package/dist/core/storage.js +15 -3
  55. package/dist/core/telemetry.d.ts +208 -0
  56. package/dist/core/telemetry.js +383 -0
  57. package/dist/core/template.d.ts +2 -3
  58. package/dist/core/template.js +2 -3
  59. package/dist/core/testing.d.ts +170 -1
  60. package/dist/core/testing.js +504 -2
  61. package/dist/db/d1.js +13 -0
  62. package/dist/db/pg.d.ts +13 -0
  63. package/dist/db/pg.js +46 -4
  64. package/dist/openapi/config.d.ts +28 -0
  65. package/dist/openapi/config.js +25 -0
  66. package/dist/openapi/doc.d.ts +40 -0
  67. package/dist/openapi/doc.js +20 -0
  68. package/dist/openapi/export.d.ts +8 -0
  69. package/dist/openapi/export.js +19 -0
  70. package/dist/openapi/gate.d.ts +15 -0
  71. package/dist/openapi/gate.js +27 -0
  72. package/dist/openapi/index.d.ts +19 -0
  73. package/dist/openapi/index.js +15 -0
  74. package/dist/openapi/openapi.config.stub +29 -0
  75. package/dist/openapi/provider.d.ts +18 -0
  76. package/dist/openapi/provider.js +35 -0
  77. package/dist/openapi/routes.d.ts +9 -0
  78. package/dist/openapi/routes.js +23 -0
  79. package/dist/openapi/spec.d.ts +23 -0
  80. package/dist/openapi/spec.js +132 -0
  81. package/dist/openapi/ui.d.ts +8 -0
  82. package/dist/openapi/ui.js +31 -0
  83. package/dist/openapi/zod.d.ts +12 -0
  84. package/dist/openapi/zod.js +46 -0
  85. package/dist/watch/config.d.ts +33 -0
  86. package/dist/watch/config.js +38 -0
  87. package/dist/watch/entry.d.ts +53 -0
  88. package/dist/watch/entry.js +105 -0
  89. package/dist/watch/gate.d.ts +20 -0
  90. package/dist/watch/gate.js +32 -0
  91. package/dist/watch/index.d.ts +21 -0
  92. package/dist/watch/index.js +17 -0
  93. package/dist/watch/migration.d.ts +7 -0
  94. package/dist/watch/migration.js +20 -0
  95. package/dist/watch/provider.d.ts +22 -0
  96. package/dist/watch/provider.js +58 -0
  97. package/dist/watch/prune.d.ts +11 -0
  98. package/dist/watch/prune.js +20 -0
  99. package/dist/watch/recorder.d.ts +24 -0
  100. package/dist/watch/recorder.js +39 -0
  101. package/dist/watch/routes.d.ts +13 -0
  102. package/dist/watch/routes.js +55 -0
  103. package/dist/watch/store.d.ts +54 -0
  104. package/dist/watch/store.js +158 -0
  105. package/dist/watch/ui/dist/watch.css +1 -0
  106. package/dist/watch/ui/dist/watch.js +555 -0
  107. package/dist/watch/ui-shell.d.ts +12 -0
  108. package/dist/watch/ui-shell.js +24 -0
  109. package/dist/watch/watch.config.stub +47 -0
  110. package/dist/watch/watchers.d.ts +12 -0
  111. package/dist/watch/watchers.js +156 -0
  112. package/docs/ai-manifest.json +737 -3
  113. package/docs/api-resources.md +118 -0
  114. package/docs/configuration.md +74 -0
  115. package/docs/console.md +193 -13
  116. package/docs/database.md +101 -0
  117. package/docs/examples/configuration.ts +40 -0
  118. package/docs/examples/console.ts +134 -0
  119. package/docs/examples/database.ts +90 -0
  120. package/docs/examples/i18n.ts +117 -0
  121. package/docs/examples/logger.ts +74 -0
  122. package/docs/examples/mail.ts +103 -0
  123. package/docs/examples/pages.ts +82 -0
  124. package/docs/examples/telemetry.ts +127 -0
  125. package/docs/examples/testing.ts +149 -0
  126. package/docs/health.md +4 -4
  127. package/docs/i18n.md +302 -0
  128. package/docs/logger.md +156 -10
  129. package/docs/mail.md +73 -0
  130. package/docs/openapi.md +111 -0
  131. package/docs/packages.md +118 -0
  132. package/docs/pages.md +217 -0
  133. package/docs/storage.md +5 -3
  134. package/docs/telemetry.md +263 -0
  135. package/docs/templates.md +3 -4
  136. package/docs/testing.md +251 -0
  137. package/docs/watch.md +118 -0
  138. package/llms-full.txt +2370 -248
  139. package/llms.txt +12 -1
  140. package/package.json +18 -2
@@ -33,17 +33,79 @@ export declare class TestResponse {
33
33
  assertHeader(name: string, value: string): this;
34
34
  /** Assert a redirect (3xx) to `location`. */
35
35
  assertRedirect(location?: string): this;
36
+ assertCreated(): this;
37
+ assertNoContent(): this;
38
+ assertBadRequest(): this;
39
+ assertUnauthorized(): this;
40
+ assertForbidden(): this;
41
+ assertNotFound(): this;
42
+ /** 422 — the status a failed `validate()` returns. */
43
+ assertUnprocessable(): this;
44
+ assertServerError(): this;
45
+ /**
46
+ * Assert the JSON body *contains* `expected` — a subset match, so a test can
47
+ * pin the fields it cares about without breaking every time an unrelated one
48
+ * is added.
49
+ *
50
+ * res.assertJsonContains({ user: { email: "a@b.com" } });
51
+ */
52
+ assertJsonContains(expected: unknown): this;
53
+ /** Assert the body contains `text` anywhere. */
54
+ assertSee(text: string): this;
55
+ /** Assert the body does *not* contain `text`. */
56
+ assertDontSee(text: string): this;
57
+ /**
58
+ * Assert a 422 carrying an error on each named field. With no fields, it just
59
+ * asserts that validation failed.
60
+ *
61
+ * res.assertValidationErrors("email", "password");
62
+ */
63
+ assertValidationErrors(...fields: string[]): this;
64
+ /** Assert there is *no* validation error on `field`. */
65
+ assertNoValidationError(field: string): this;
66
+ assertHeaderMissing(name: string): this;
67
+ /** The cookies this response set, by name. */
68
+ cookies(): Record<string, string>;
69
+ /** Assert the response set a cookie — with `value`, if you give one. */
70
+ assertCookie(name: string, value?: string): this;
71
+ assertCookieMissing(name: string): this;
72
+ /** Print status, headers, and body — for when a test fails and you're stuck. */
73
+ dump(): this;
74
+ }
75
+ /** Defaults applied to every request a `TestClient` makes. */
76
+ interface ClientDefaults {
77
+ headers: Record<string, string>;
78
+ cookies: Record<string, string>;
36
79
  }
37
80
  /** Injects requests into an app and returns `TestResponse`s. */
38
81
  export declare class TestClient {
39
82
  private target;
40
- constructor(target: Requestable);
83
+ private defaults;
84
+ constructor(target: Requestable, defaults?: ClientDefaults);
85
+ /** A copy of this client with extra default headers. */
86
+ withHeaders(headers: Record<string, string>): TestClient;
87
+ withHeader(name: string, value: string): TestClient;
88
+ /** A copy of this client that sends these cookies. */
89
+ withCookies(cookies: Record<string, string>): TestClient;
90
+ withCookie(name: string, value: string): TestClient;
91
+ /** Send `Authorization: Bearer <token>` — the shorthand for an API test. */
92
+ withToken(token: string): TestClient;
93
+ /** Send HTTP basic credentials. */
94
+ withBasicAuth(username: string, password: string): TestClient;
95
+ /** Ask for JSON back — sets `Accept: application/json`. */
96
+ acceptJson(): TestClient;
97
+ /** Merge the client's defaults into one request's init. */
98
+ private init;
41
99
  request(path: string, init?: RequestInit): Promise<TestResponse>;
42
100
  get(path: string, init?: RequestInit): Promise<TestResponse>;
43
101
  delete(path: string, init?: RequestInit): Promise<TestResponse>;
44
102
  post(path: string, body?: unknown, init?: RequestInit): Promise<TestResponse>;
45
103
  put(path: string, body?: unknown, init?: RequestInit): Promise<TestResponse>;
46
104
  patch(path: string, body?: unknown, init?: RequestInit): Promise<TestResponse>;
105
+ /** POST a URL-encoded form, the way a browser would. */
106
+ form(path: string, fields: Record<string, string | number | boolean>, init?: RequestInit): Promise<TestResponse>;
107
+ /** POST a multipart form — for file uploads. */
108
+ multipart(path: string, fields: Record<string, string | Blob>, init?: RequestInit): Promise<TestResponse>;
47
109
  }
48
110
  /**
49
111
  * Build a `TestClient` from an `Application`, an `HttpKernel`, or anything with a
@@ -51,4 +113,111 @@ export declare class TestClient {
51
113
  * `HttpKernel`; pass a kernel yourself if you need global middleware registered.
52
114
  */
53
115
  export declare function testClient(target: Application | HttpKernel | Requestable): TestClient;
116
+ /**
117
+ * Put the framework's global state back the way it was, so one test can't leak
118
+ * into the next. Call it in an `afterEach`.
119
+ *
120
+ * afterEach(() => resetState());
121
+ *
122
+ * It restores every fake (disk, queue, mail, hash, time), drops event listeners,
123
+ * empties the cache, and gives you a fresh lock store. It does **not** touch the
124
+ * database — see `refreshDatabase()`.
125
+ */
126
+ export declare function resetState(): void;
127
+ /** Whether time is currently frozen. */
128
+ export declare function timeIsFrozen(): boolean;
129
+ /**
130
+ * Freeze the clock, so anything that reads the time sees the same instant —
131
+ * which is how you test a token that expires in an hour without waiting an hour.
132
+ *
133
+ * freezeTime("2026-07-11T12:00:00Z");
134
+ * timeTravel(3600_000); // an hour later
135
+ * restoreTime();
136
+ *
137
+ * Mocks `Date` and `Date.now()`. It does **not** mock timers: a `setTimeout` still
138
+ * fires on the real clock.
139
+ */
140
+ export declare function freezeTime(at?: Date | number | string): number;
141
+ /** Move the frozen clock forward (or back, with a negative value) by milliseconds. */
142
+ export declare function timeTravel(ms: number): number;
143
+ /** Hand the real clock back. */
144
+ export declare function restoreTime(): void;
145
+ /** Assert at least one row in `table` matches `where` (an exact `count` if given). */
146
+ export declare function assertDatabaseHas(table: string, where: Record<string, unknown>, count?: number): Promise<void>;
147
+ /** Assert no row in `table` matches `where`. */
148
+ export declare function assertDatabaseMissing(table: string, where: Record<string, unknown>): Promise<void>;
149
+ /** Assert `table` holds exactly `count` rows. */
150
+ export declare function assertDatabaseCount(table: string, count: number): Promise<void>;
151
+ /** Assert `table` is empty. */
152
+ export declare function assertDatabaseEmpty(table: string): Promise<void>;
153
+ /**
154
+ * Empty tables between tests. Order matters when foreign keys do — pass children
155
+ * before parents.
156
+ *
157
+ * afterEach(() => truncate("comments", "posts", "users"));
158
+ *
159
+ * This deletes rows; it doesn't roll back a transaction, so it works on every
160
+ * driver (D1, Postgres, libSQL) rather than only the ones with savepoints.
161
+ */
162
+ export declare function truncate(...tables: string[]): Promise<void>;
163
+ /** A recording stand-in for a function. */
164
+ export interface Spy<A extends unknown[] = unknown[], R = unknown> {
165
+ (...args: A): R;
166
+ /** The arguments of every call, in order. */
167
+ readonly calls: A[];
168
+ readonly callCount: number;
169
+ /** Whether it was called at all. */
170
+ readonly called: boolean;
171
+ /** Whether any call had exactly these arguments. */
172
+ calledWith(...args: A): boolean;
173
+ /** Make it return something (or something new). */
174
+ returns(value: R): Spy<A, R>;
175
+ reset(): void;
176
+ }
177
+ /**
178
+ * A function that records how it was called — the smallest useful test double.
179
+ *
180
+ * const send = spy<[string], void>();
181
+ * notify(send);
182
+ * assert.equal(send.callCount, 1);
183
+ * assert.ok(send.calledWith("hello"));
184
+ */
185
+ export declare function spy<A extends unknown[] = unknown[], R = unknown>(implementation?: (...args: A) => R): Spy<A, R>;
186
+ export declare function spyOn<T extends object, K extends keyof T>(target: T, method: K): Spy<unknown[], unknown>;
187
+ /** Undo every `spyOn()`. */
188
+ export declare function restoreSpies(): void;
189
+ /** What a command did: its output, its error output, and its exit code. */
190
+ export declare class CommandResult {
191
+ readonly stdout: string[];
192
+ readonly stderr: string[];
193
+ readonly exitCode: number;
194
+ constructor(stdout: string[], stderr: string[], exitCode: number);
195
+ /** Everything written to stdout, joined. */
196
+ output(): string;
197
+ assertExitCode(expected: number): this;
198
+ /** Exit code 0. */
199
+ assertSucceeded(): this;
200
+ /** A non-zero exit code. */
201
+ assertFailed(): this;
202
+ /** Assert a line of output contains `text`. */
203
+ assertOutputContains(text: string): this;
204
+ assertOutputMatches(pattern: RegExp): this;
205
+ assertErrorContains(text: string): this;
206
+ }
207
+ /**
208
+ * Run a console command in-process and capture what it printed and the exit code
209
+ * it set — no subprocess, so it's fast and you can assert on it.
210
+ *
211
+ * You pass the command in, because the console entry point belongs to your app
212
+ * (it's the thing that knows how to build your application), not to the core:
213
+ *
214
+ * import { run } from "@shaferllc/keel/core/cli"; // or your own bin
215
+ *
216
+ * const result = await runCommand(() => run(["node", "keel", "routes"]));
217
+ * result.assertSucceeded().assertOutputContains("GET /users");
218
+ *
219
+ * `console.log`/`warn` are captured as stdout, `console.error` as stderr, and
220
+ * `process.exitCode` is read and then restored.
221
+ */
222
+ export declare function runCommand(command: () => unknown | Promise<unknown>): Promise<CommandResult>;
54
223
  export {};