@orieljs/framework 0.1.0-alpha.6 → 0.1.0-alpha.7

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 (86) hide show
  1. package/NOTICE +4 -0
  2. package/README.md +10 -1
  3. package/TRADEMARKS.md +33 -0
  4. package/dist/{chunk-ZOEPKBVF.js → chunk-4MWG3B7G.js} +1347 -428
  5. package/dist/chunk-4MWG3B7G.js.map +1 -0
  6. package/dist/{chunk-POEP4B2M.js → chunk-7ZQZYDOC.js} +745 -105
  7. package/dist/chunk-7ZQZYDOC.js.map +1 -0
  8. package/dist/{chunk-I63ZFMX6.js → chunk-GVNLZZTN.js} +1 -1
  9. package/dist/chunk-GVNLZZTN.js.map +1 -0
  10. package/dist/{chunk-MFTZQMZM.js → chunk-VOYW3R5J.js} +4 -4
  11. package/dist/chunk-VOYW3R5J.js.map +1 -0
  12. package/dist/contracts.d.ts +515 -17
  13. package/dist/contracts.js +1 -1
  14. package/dist/index.d-WQpfdIbG.d.ts +630 -0
  15. package/dist/index.d.ts +964 -152
  16. package/dist/index.js +4279 -866
  17. package/dist/index.js.map +1 -1
  18. package/dist/{jsx-runtime-CBZQYYDS.d.ts → jsx-runtime-6um4Mwfb.d.ts} +96 -5
  19. package/dist/jsx-runtime.d.ts +2 -2
  20. package/dist/jsx-runtime.js +3 -3
  21. package/dist/{testing-BM481Hdz.d.ts → testing-CKwfWJGn.d.ts} +180 -15
  22. package/dist/testing.d.ts +2 -2
  23. package/dist/testing.js +3 -3
  24. package/docs/authentication.md +487 -0
  25. package/docs/authorization.md +435 -0
  26. package/docs/cache.md +647 -0
  27. package/docs/collections.md +2270 -0
  28. package/docs/configuration.md +334 -0
  29. package/docs/console.md +651 -0
  30. package/docs/container.md +430 -0
  31. package/docs/controllers.md +406 -0
  32. package/docs/csrf.md +126 -0
  33. package/docs/database-testing.md +144 -0
  34. package/docs/database.md +236 -0
  35. package/docs/deployment.md +276 -0
  36. package/docs/eloquent-collections.md +218 -0
  37. package/docs/eloquent-factories.md +255 -0
  38. package/docs/eloquent-mutators.md +254 -0
  39. package/docs/eloquent-observers.md +145 -0
  40. package/docs/eloquent-relationships.md +557 -0
  41. package/docs/eloquent.md +494 -0
  42. package/docs/encryption.md +104 -0
  43. package/docs/errors.md +318 -0
  44. package/docs/events.md +336 -0
  45. package/docs/facades.md +278 -0
  46. package/docs/hashing.md +118 -0
  47. package/docs/helpers.md +1281 -0
  48. package/docs/http-tests.md +386 -0
  49. package/docs/inertia.md +485 -0
  50. package/docs/installation.md +209 -0
  51. package/docs/lifecycle.md +234 -0
  52. package/docs/live-tests.md +226 -0
  53. package/docs/live.md +910 -0
  54. package/docs/logging.md +255 -0
  55. package/docs/mail.md +619 -0
  56. package/docs/middleware.md +384 -0
  57. package/docs/migrations.md +416 -0
  58. package/docs/mocking.md +264 -0
  59. package/docs/navigation.json +273 -0
  60. package/docs/notifications.md +225 -0
  61. package/docs/pagination.md +247 -0
  62. package/docs/passwords.md +207 -0
  63. package/docs/providers.md +187 -0
  64. package/docs/queries.md +600 -0
  65. package/docs/queues.md +330 -0
  66. package/docs/requests.md +533 -0
  67. package/docs/responses.md +444 -0
  68. package/docs/routing.md +738 -0
  69. package/docs/scheduling.md +277 -0
  70. package/docs/seeding.md +122 -0
  71. package/docs/session.md +409 -0
  72. package/docs/starter-kits.md +396 -0
  73. package/docs/storage.md +192 -0
  74. package/docs/structure.md +264 -0
  75. package/docs/testing.md +165 -0
  76. package/docs/time-travel.md +138 -0
  77. package/docs/validation.md +1792 -0
  78. package/docs/verification.md +152 -0
  79. package/docs/views.md +566 -0
  80. package/docs/vite.md +322 -0
  81. package/package.json +34 -32
  82. package/dist/chunk-I63ZFMX6.js.map +0 -1
  83. package/dist/chunk-MFTZQMZM.js.map +0 -1
  84. package/dist/chunk-POEP4B2M.js.map +0 -1
  85. package/dist/chunk-ZOEPKBVF.js.map +0 -1
  86. package/dist/index.d-BJyJx6U1.d.ts +0 -319
@@ -41,14 +41,26 @@ interface Session {
41
41
  start(): Promise<void>;
42
42
  save(): Promise<void>;
43
43
  all(): Record<string, unknown>;
44
+ only(keys: string[]): Record<string, unknown>;
45
+ except(keys: string[]): Record<string, unknown>;
46
+ /** Whether every key is present, even with a null value. */
47
+ exists(...keys: string[]): boolean;
48
+ missing(...keys: string[]): boolean;
49
+ /** Whether the key is present and not null. */
44
50
  has(key: string): boolean;
45
51
  get<T = unknown>(key: string, defaultValue?: T): T;
46
52
  put(key: string | Record<string, unknown>, value?: unknown): void;
47
53
  push(key: string, value: unknown): void;
54
+ /** Adds `amount` (default 1) to the number under the key and returns the new value. */
55
+ increment(key: string, amount?: number): number;
56
+ decrement(key: string, amount?: number): number;
48
57
  pull<T = unknown>(key: string, defaultValue?: T): T;
58
+ /** Forgets the key and returns its value. */
59
+ remove<T = unknown>(key: string): T;
49
60
  forget(...keys: string[]): void;
50
61
  flush(): void;
51
- flash(key: string, value: unknown): void;
62
+ /** Puts the value (true when omitted) for the next request only. */
63
+ flash(key: string, value?: unknown): void;
52
64
  now(key: string, value: unknown): void;
53
65
  reflash(): void;
54
66
  keep(...keys: string[]): void;
@@ -70,6 +82,370 @@ interface SessionHandler {
70
82
  gc(maxLifetimeSeconds: number): Promise<void>;
71
83
  }
72
84
 
85
+ /**
86
+ * What one rule tells the validated data's type about a value that passed it. Every member is optional:
87
+ * - `value`: the type of a passing value. A field's type is the intersection of its rules' values
88
+ * (`'string|in:a,b'` is `'a' | 'b'`); a field whose rules set none is `unknown`.
89
+ * - `presence`: `'required'` puts the key in the data and rules out null (`required`, `accepted`); `'present'` puts
90
+ * the key in the data; `'filled'` rules out null without putting the key in; `'excluded'` leaves the key out;
91
+ * `'optional'` may leave the key out whatever else would put it in (`sometimes` skips a missing key, and a rule that
92
+ * excludes the field under a condition drops it with its children), so the key is optional and doesn't put its
93
+ * parent in. A key nothing puts in is optional, as the validated data has only the keys the input had.
94
+ * - `each`: on a field that also has `array`, the type of each item instead of `value` (`in` checks every item of an
95
+ * array: `'array|in:a,b'` is `Array<'a' | 'b'>`).
96
+ * - `nullable`: null passes, unless a `'required'` or `'filled'` rule says otherwise.
97
+ */
98
+ interface RuleTyping {
99
+ value?: unknown;
100
+ each?: unknown;
101
+ presence?: 'required' | 'present' | 'filled' | 'excluded' | 'optional';
102
+ nullable?: true;
103
+ }
104
+ /** A number, or the text of one: a form sends every field as text, and `integer` and `numeric` accept both. */
105
+ type NumericValue = number | `${number}`;
106
+ /** `a,b,c` → `'a' | 'b' | 'c'`: a rule's comma-separated parameters, as Validator splits them. */
107
+ type RuleParameterList<P extends string> = P extends `${infer Head},${infer Tail}` ? Head | RuleParameterList<Tail> : P;
108
+ /**
109
+ * What `in` (and `Rule.in()`) lets through: it compares the value's text with each listed value, so `'1'` lets the
110
+ * number 1 through too, and `'true'` the boolean. A list known only as `string` (`` `in:${list.join(',')}` ``) says
111
+ * nothing, as any value whose text is listed passes.
112
+ */
113
+ type InValue<V> = string extends V ? unknown : V extends string ? V | (V extends `${infer N extends number}` ? N : never) | (V extends 'true' ? true : V extends 'false' ? false : never) : V extends number | boolean ? V | `${V}` : V;
114
+ /**
115
+ * The string rules that tell the validated data's type something, by name, each a RuleTyping. `Parameters` is the
116
+ * rule's text after the colon (`'a,b'` for `in:a,b`). A rule that only checks a value it doesn't change the type of
117
+ * (`max`, `confirmed`, `unique`) has no entry. Add a member for a new type-setting rule; an app adds its own
118
+ * `Validator.extend()` rules by declaration merging, with the same type parameter.
119
+ */
120
+ interface ValidationRuleTypes<Parameters extends string = string> {
121
+ required: {
122
+ presence: 'required';
123
+ };
124
+ /** Skips the field when the key is missing, so the key may be left out even with `required`. */
125
+ sometimes: {
126
+ presence: 'optional';
127
+ };
128
+ nullable: {
129
+ nullable: true;
130
+ };
131
+ accepted: {
132
+ presence: 'required';
133
+ value: true | 1 | 'yes' | 'on' | '1' | 'true';
134
+ };
135
+ declined: {
136
+ presence: 'required';
137
+ value: false | 0 | 'no' | 'off' | '0' | 'false';
138
+ };
139
+ present: {
140
+ presence: 'present';
141
+ };
142
+ filled: {
143
+ presence: 'filled';
144
+ };
145
+ missing: {
146
+ presence: 'excluded';
147
+ };
148
+ exclude: {
149
+ presence: 'excluded';
150
+ };
151
+ /** Drop the field, children and all, under a condition, so the key may be left out even with `required`. */
152
+ exclude_if: {
153
+ presence: 'optional';
154
+ };
155
+ exclude_unless: {
156
+ presence: 'optional';
157
+ };
158
+ exclude_with: {
159
+ presence: 'optional';
160
+ };
161
+ exclude_without: {
162
+ presence: 'optional';
163
+ };
164
+ /**
165
+ * Passes only when the field is missing or empty, so a value that is there is null, blank or whitespace-only text,
166
+ * [] or {}.
167
+ */
168
+ prohibited: {
169
+ value: null | string | [] | Record<string, never>;
170
+ };
171
+ string: {
172
+ value: string;
173
+ };
174
+ /** `integer:strict` and `numeric:strict` refuse the text of a number. */
175
+ integer: {
176
+ value: Parameters extends 'strict' ? number : NumericValue;
177
+ };
178
+ numeric: {
179
+ value: Parameters extends 'strict' ? number : NumericValue;
180
+ };
181
+ /** `boolean:strict` lets only true and false through. */
182
+ boolean: {
183
+ value: Parameters extends 'strict' ? boolean : boolean | 0 | 1 | '0' | '1';
184
+ };
185
+ /** `array` is a list; `array:a,b` an object whose keys are among those named. */
186
+ array: {
187
+ value: Parameters extends '' ? unknown[] : {
188
+ [K in RuleParameterList<Parameters>]?: unknown;
189
+ };
190
+ };
191
+ list: {
192
+ value: unknown[];
193
+ };
194
+ contains: {
195
+ value: unknown[];
196
+ };
197
+ doesnt_contain: {
198
+ value: unknown[];
199
+ };
200
+ required_array_keys: {
201
+ value: {
202
+ [K in RuleParameterList<Parameters>]: unknown;
203
+ };
204
+ };
205
+ in_array_keys: {
206
+ value: Record<string, unknown>;
207
+ };
208
+ email: {
209
+ value: string;
210
+ };
211
+ url: {
212
+ value: string;
213
+ };
214
+ uuid: {
215
+ value: string;
216
+ };
217
+ alpha: {
218
+ value: string;
219
+ };
220
+ alpha_num: {
221
+ value: string;
222
+ };
223
+ alpha_dash: {
224
+ value: string;
225
+ };
226
+ regex: {
227
+ value: string;
228
+ };
229
+ lowercase: {
230
+ value: string | number;
231
+ };
232
+ uppercase: {
233
+ value: string | number;
234
+ };
235
+ ascii: {
236
+ value: string | number;
237
+ };
238
+ hex_color: {
239
+ value: string;
240
+ };
241
+ json: {
242
+ value: string;
243
+ };
244
+ ip: {
245
+ value: string;
246
+ };
247
+ ipv4: {
248
+ value: string;
249
+ };
250
+ ipv6: {
251
+ value: string;
252
+ };
253
+ mac_address: {
254
+ value: string;
255
+ };
256
+ ulid: {
257
+ value: string;
258
+ };
259
+ timezone: {
260
+ value: string;
261
+ };
262
+ active_url: {
263
+ value: string;
264
+ };
265
+ digits: {
266
+ value: NumericValue;
267
+ };
268
+ digits_between: {
269
+ value: NumericValue;
270
+ };
271
+ max_digits: {
272
+ value: NumericValue;
273
+ };
274
+ min_digits: {
275
+ value: NumericValue;
276
+ };
277
+ decimal: {
278
+ value: NumericValue;
279
+ };
280
+ multiple_of: {
281
+ value: NumericValue;
282
+ };
283
+ date: {
284
+ value: string;
285
+ };
286
+ /** A number passes a format made of digits (`Y`, `U`), as in Laravel. */
287
+ date_format: {
288
+ value: string | number;
289
+ };
290
+ /** A number passes the comparisons as a timestamp, as in Laravel; with `date` in the set, the value is text. */
291
+ before: {
292
+ value: string | number;
293
+ };
294
+ before_or_equal: {
295
+ value: string | number;
296
+ };
297
+ after: {
298
+ value: string | number;
299
+ };
300
+ after_or_equal: {
301
+ value: string | number;
302
+ };
303
+ date_equals: {
304
+ value: string | number;
305
+ };
306
+ current_password: {
307
+ value: string;
308
+ };
309
+ in: {
310
+ value: InValue<RuleParameterList<Parameters>>;
311
+ each: InValue<RuleParameterList<Parameters>>;
312
+ };
313
+ file: {
314
+ value: UploadedFile;
315
+ };
316
+ image: {
317
+ value: UploadedFile;
318
+ };
319
+ mimes: {
320
+ value: UploadedFile;
321
+ };
322
+ mimetypes: {
323
+ value: UploadedFile;
324
+ };
325
+ extensions: {
326
+ value: UploadedFile;
327
+ };
328
+ dimensions: {
329
+ value: UploadedFile;
330
+ };
331
+ }
332
+ /**
333
+ * A rule object that tells the validated data's type what it lets through, as a string rule's entry in
334
+ * ValidationRuleTypes does. `__typing` is never set or read at run time: declare it (`declare readonly __typing?:
335
+ * { value: string }`). A rule object without it changes nothing, so its field takes the type of the string rules
336
+ * beside it (`['required', 'string', new Uppercase()]` is a string).
337
+ */
338
+ interface TypedValidationRule<T extends RuleTyping = RuleTyping> extends ValidationRule {
339
+ readonly __typing?: T;
340
+ }
341
+ type RuleName<R extends string> = R extends `${infer Name}:${string}` ? Name : R;
342
+ type RuleParameters<R extends string> = R extends `${string}:${infer Parameters}` ? Parameters : '';
343
+ type PipeRules<S extends string> = S extends `${infer Head}|${infer Tail}` ? Head | PipeRules<Tail> : S;
344
+ /** What a rule without an entry, or a rule object without `__typing`, says: nothing. */
345
+ type NoTyping = Record<never, never>;
346
+ type StringRuleTyping<R extends string> = R extends unknown ? RuleName<R> extends keyof ValidationRuleTypes ? ValidationRuleTypes<RuleParameters<R>>[RuleName<R>] : NoTyping : never;
347
+ type ObjectRuleTyping<O> = '__typing' extends keyof O ? Exclude<O['__typing' & keyof O], undefined> : NoTyping;
348
+ type ItemTyping<I> = I extends string ? StringRuleTyping<I> : ObjectRuleTyping<I>;
349
+ /** The typings of one field's rules, as a union: `'required|string'` or `['required', 'string', rule]`. */
350
+ type SpecTypings<Spec> = Spec extends string ? StringRuleTyping<PipeRules<Spec>> : Spec extends ReadonlyArray<infer Item> ? ItemTyping<Item> : NoTyping;
351
+ /** A rule string the compiler only knows as `string` (rules written without `as const`) says nothing. */
352
+ type IsWide<Spec> = Spec extends string ? (string extends Spec ? true : false) : Spec extends ReadonlyArray<infer Item> ? (string extends Extract<Item, string> ? true : false) : false;
353
+ type Intersect<U> = (U extends unknown ? (value: U) => void : never) extends (value: infer I) => void ? I : never;
354
+ type Boxed<T> = T extends {
355
+ value: infer V;
356
+ } ? {
357
+ v: V;
358
+ } : never;
359
+ type BoxedEach<T> = T extends {
360
+ each: infer E;
361
+ } ? {
362
+ v: E;
363
+ } : never;
364
+ type Unbox<B> = [B] extends [never] ? unknown : Intersect<B> extends {
365
+ v: infer V;
366
+ } ? V : never;
367
+ type WithoutEach<T> = T extends {
368
+ each: unknown;
369
+ } ? never : T;
370
+ /** The intersection of the rules' values; on an array, the rules with `each` type its items instead. */
371
+ type ValueOf<T> = [BoxedEach<T>] extends [never] ? Unbox<Boxed<T>> : Unbox<Boxed<WithoutEach<T>>> extends readonly unknown[] ? Array<Unbox<BoxedEach<T>>> : Unbox<Boxed<T>>;
372
+ type PresenceOf<T> = T extends {
373
+ presence: infer P;
374
+ } ? P : never;
375
+ type NullableOf<T> = T extends {
376
+ nullable: true;
377
+ } ? true : never;
378
+ interface FieldInfo {
379
+ value: unknown;
380
+ required: boolean;
381
+ optional: boolean;
382
+ nullable: boolean;
383
+ excluded: boolean;
384
+ }
385
+ type FieldFrom<T, Optional = [Extract<PresenceOf<T>, 'optional'>] extends [never] ? false : true> = {
386
+ value: ValueOf<T>;
387
+ required: Optional extends true ? false : [Extract<PresenceOf<T>, 'required' | 'present'>] extends [never] ? false : true;
388
+ optional: Optional;
389
+ nullable: [NullableOf<T>] extends [never] ? false : [Extract<PresenceOf<T>, 'required' | 'filled'>] extends [never] ? true : false;
390
+ excluded: [Extract<PresenceOf<T>, 'excluded'>] extends [never] ? false : true;
391
+ };
392
+ /** Rules the compiler only knows as `string` say nothing, not even that the key is there. */
393
+ type FieldOf<Spec> = IsWide<Spec> extends true ? NoRules : FieldFrom<SpecTypings<Spec>>;
394
+ type NoRules = {
395
+ value: unknown;
396
+ required: false;
397
+ optional: false;
398
+ nullable: false;
399
+ excluded: false;
400
+ };
401
+ type Keys<R> = Extract<keyof R, string>;
402
+ type Head<K extends string> = K extends `${infer H}.${string}` ? H : K;
403
+ type Heads<R> = Head<Keys<R>>;
404
+ type Children<R, H extends string> = {
405
+ [K in Keys<R> as K extends `${H}.${infer Rest}` ? Rest : never]: R[K];
406
+ };
407
+ type HasChildren<R, H extends string> = [Keys<Children<R, H>>] extends [never] ? false : true;
408
+ type Own<R, H extends string> = H extends keyof R ? FieldOf<R[H]> : NoRules;
409
+ type OrNull<T, Nullable> = Nullable extends true ? T | null : T;
410
+ /**
411
+ * A wildcard's rules (`items.*.name`) make an array of what they describe; dotted keys (`author.name`) an object.
412
+ * Without a rule that makes the parent a list (`array`, `list`), validated() may give an object instead: an object
413
+ * sent under `items.*` stays one, and a sparse match (`{ items: [{}, { qty: 1 }] }`) keeps its index keys.
414
+ */
415
+ type Structure<C, Parent> = '*' extends Heads<C> ? Items<ValueAt<C, '*'>, Parent> : Shape<C>;
416
+ type Items<V, Parent> = [Parent] extends [readonly unknown[]] ? V[] : V[] | Record<string, V>;
417
+ type ValueAt<R, H extends string, F extends FieldInfo = Own<R, H>> = OrNull<HasChildren<R, H> extends true ? Structure<Children<R, H>, F['value']> : F['value'], F['nullable']>;
418
+ /**
419
+ * A key is in the data when a rule puts it there, or when a child key without a wildcard is required, unless a rule
420
+ * may leave it out.
421
+ */
422
+ type IsRequired<R, H extends string> = Own<R, H>['optional'] extends true ? false : Own<R, H>['required'] extends true ? true : HasChildren<R, H> extends true ? '*' extends Heads<Children<R, H>> ? false : [RequiredHeads<Children<R, H>>] extends [never] ? false : true : false;
423
+ type RequiredHeads<R> = {
424
+ [H in Heads<R>]: Own<R, H>['excluded'] extends true ? never : IsRequired<R, H> extends true ? H : never;
425
+ }[Heads<R>];
426
+ type Simplify<T> = {
427
+ [K in keyof T]: T[K];
428
+ } & {};
429
+ type Shape<R> = string extends Keys<R> ? Record<string, unknown> : Simplify<{
430
+ [H in Heads<R> as Own<R, H>['excluded'] extends true ? never : IsRequired<R, H> extends true ? H : never]: ValueAt<R, H>;
431
+ } & {
432
+ [H in Heads<R> as Own<R, H>['excluded'] extends true ? never : IsRequired<R, H> extends true ? never : H]?: ValueAt<R, H>;
433
+ }>;
434
+ /**
435
+ * The type of one field's passing value, from its rule string: `'nullable|integer'` → `number | \`${number}\` | null`.
436
+ * A rule string the compiler only knows as `string` gives unknown.
437
+ */
438
+ type InferRuleType<S extends string> = string extends S ? unknown : OrNull<FieldOf<S>['value'], FieldOf<S>['nullable']>;
439
+ /**
440
+ * The validated data a Rules object gives: nested as Validator's validated() nests it (`author.name` → `{ author: {
441
+ * name } }`, `tags.*` → an array, or an object too unless `tags` has `array` or `list`), each key optional unless a
442
+ * rule puts it in, each value typed by its rules. Rules known only as `Rules` (or any `Record<string, …>`) give
443
+ * `Record<string, unknown>`, and rule strings known only as `string` give optional keys of unknown. Write a
444
+ * FormRequest's `rules()` with `as const` so its rule strings keep their text; `request().validate({ … })` needs
445
+ * nothing.
446
+ */
447
+ type InferValidatedType<R> = R extends unknown ? Shape<R> : never;
448
+
73
449
  /** A rule object (Laravel's ValidationRule). Call fail() to reject the value. */
74
450
  interface ValidationRule {
75
451
  validate(attribute: string, value: unknown, fail: (message: string) => void, data: Record<string, unknown>): void | Promise<void>;
@@ -77,6 +453,7 @@ interface ValidationRule {
77
453
  /** 'required|string|max:255', or an array mixing strings and rule objects. Keys support dot + `*` wildcards. */
78
454
  type Rules = Record<string, string | ReadonlyArray<string | ValidationRule>>;
79
455
  interface MessageBag {
456
+ add(key: string, message: string): void;
80
457
  has(key: string): boolean;
81
458
  first(key?: string): string | undefined;
82
459
  get(key: string): string[];
@@ -85,18 +462,21 @@ interface MessageBag {
85
462
  count(): number;
86
463
  toObject(): Record<string, string[]>;
87
464
  }
88
- interface Validator {
465
+ /** `R` is the rules' type: `validated()` is typed from them, as `request().validate()` is. */
466
+ interface Validator<R extends Rules = Rules> {
89
467
  passes(): Promise<boolean>;
90
468
  fails(): Promise<boolean>;
91
469
  /** Only the keys that have rules. Throws ValidationError (Exceptions.ts) on failure. */
92
- validated<T = Record<string, unknown>>(): Promise<T>;
470
+ validated<T = InferValidatedType<R>>(): Promise<T>;
93
471
  /** Valid only after passes()/fails()/validated() has run. */
94
472
  errors(): MessageBag;
95
473
  after(callback: (validator: Validator) => void | Promise<void>): this;
474
+ /** Adds a failure message for `field` (from an after() hook). */
475
+ fail(field: string, message: string): void;
96
476
  }
97
477
  type RuleCallback = (attribute: string, value: unknown, parameters: string[], data: Record<string, unknown>) => boolean | Promise<boolean>;
98
478
  interface ValidationFactory {
99
- make(data: Record<string, unknown>, rules: Rules, messages?: Record<string, string>, attributes?: Record<string, string>): Validator;
479
+ make<const R extends Rules>(data: Record<string, unknown>, rules: R, messages?: Record<string, string>, attributes?: Record<string, string>): Validator<R>;
100
480
  /** Registers a string rule, e.g. extend('uppercase', fn, ':attribute must be uppercase'). */
101
481
  extend(name: string, callback: RuleCallback, message?: string): void;
102
482
  }
@@ -110,7 +490,13 @@ interface UploadedFile {
110
490
  readonly mimeType: string;
111
491
  readonly size: number;
112
492
  extension(): string;
493
+ /** The extension of the name the browser gave the file, lower-cased ('' without one). */
494
+ clientExtension(): string;
495
+ /** A random 40-character name with extension(): the name store() gives the file. */
496
+ hashName(): string;
113
497
  arrayBuffer(): Promise<ArrayBuffer>;
498
+ text(): Promise<string>;
499
+ stream(): ReadableStream<Uint8Array>;
114
500
  /** Stores on a Filesystem disk. Returns the stored path. */
115
501
  store(directory: string, disk?: string): Promise<string>;
116
502
  storeAs(directory: string, name: string, disk?: string): Promise<string>;
@@ -154,11 +540,18 @@ interface HttpRequest {
154
540
  /** Route parameter (after model binding), or every parameter when called with no key. */
155
541
  route<T = unknown>(key?: string): T;
156
542
  session(): Session;
157
- /** The user resolved earlier by auth middleware. Undefined for guests. Use `await Auth.user()` to resolve lazily. */
158
- user<T = unknown>(guard?: string): T | undefined;
543
+ /**
544
+ * The signed-in user, found through the request's guard (the default one, or the one named) the first time it is
545
+ * asked for, then the same instance for the rest of the request. Undefined for a guest.
546
+ */
547
+ user<T = unknown>(guard?: string): Promise<T | undefined>;
548
+ /** How user() finds the user (Auth sets it). The resolver may return a promise. */
159
549
  setUserResolver(resolver: (guard?: string) => unknown): void;
160
- /** Validates this request's input via the ValidationFactory; throws a Validation OrielError (422) on failure. */
161
- validate<T = Record<string, unknown>>(rules: Rules, messages?: Record<string, string>, attributes?: Record<string, string>): Promise<T>;
550
+ /**
551
+ * Validates this request's input via the ValidationFactory; throws a Validation OrielError (422) on failure. The
552
+ * validated data is typed from the rules: `validate({ title: 'required|string' })` is `{ title: string }`.
553
+ */
554
+ validate<const R extends Rules>(rules: R, messages?: Record<string, string>, attributes?: Record<string, string>): Promise<InferValidatedType<R>>;
162
555
  }
163
556
  declare const HttpRequest: Token<HttpRequest>;
164
557
  type Next = (request: HttpRequest) => Promise<Response>;
@@ -187,6 +580,8 @@ interface MiddlewareConfigurator {
187
580
  except: string[];
188
581
  }): this;
189
582
  redirectGuestsTo(url: string | ((request: HttpRequest) => string)): this;
583
+ /** Where the `guest` middleware sends a signed-in user. */
584
+ redirectUsersTo(url: string | ((request: HttpRequest) => string)): this;
190
585
  }
191
586
  interface HttpKernel {
192
587
  handle(request: Request): Promise<Response>;
@@ -197,7 +592,11 @@ interface ResponseFactory {
197
592
  make(content?: BodyInit | null, status?: number, headers?: HeadersInit): Response;
198
593
  json(data: unknown, status?: number, headers?: HeadersInit): Response;
199
594
  noContent(status?: number): Response;
595
+ /** Renders the view into an HTML response. */
596
+ view(name: string, data?: Record<string, unknown>, status?: number, headers?: HeadersInit): Promise<Response>;
200
597
  download(path: string, name?: string, headers?: HeadersInit): Promise<Response>;
598
+ /** Sends the file inline, for the browser to show (download() sends it as an attachment). */
599
+ file(path: string, headers?: HeadersInit): Promise<Response>;
201
600
  stream(body: ReadableStream, status?: number, headers?: HeadersInit): Response;
202
601
  }
203
602
  declare const ResponseFactory: Token<ResponseFactory>;
@@ -240,6 +639,13 @@ interface Application extends Container {
240
639
  /** Register a callback that terminate() runs, in registration order. */
241
640
  terminating(callback: () => void | Promise<void>): void;
242
641
  terminate(): Promise<void>;
642
+ /**
643
+ * Register a callback that shutdown() runs once, before the process exits: close pools and clients, write out
644
+ * files. terminating() callbacks run after every request; these run once, at the end.
645
+ */
646
+ onShutdown(callback: () => void | Promise<void>): void;
647
+ /** Runs the shutdown callbacks once, newest first; a failing one is printed and the rest still run. Never rejects. */
648
+ shutdown(): Promise<void>;
243
649
  }
244
650
  declare const Application: Token<Application>;
245
651
  interface ExceptionHandler {
@@ -434,6 +840,27 @@ interface RouteList {
434
840
  }
435
841
  /** A route name: the generated ones, or any string before `oriel route:types` has run. */
436
842
  type RouteName = [keyof RouteList] extends [never] ? string : Extract<keyof RouteList, string>;
843
+ /** A route parameter's value: a scalar, or a model (its route key, or the `{post:slug}` column, goes in the URL). */
844
+ type RouteParameter = string | number | bigint | boolean | {
845
+ getRouteKey(): unknown;
846
+ };
847
+ type GeneratedParameters<N extends keyof RouteList> = {
848
+ [K in keyof RouteList[N]]: RouteParameter;
849
+ };
850
+ type RequiredParameters<P> = {
851
+ [K in keyof P]-?: Record<never, never> extends Pick<P, K> ? never : K;
852
+ }[keyof P];
853
+ /**
854
+ * A named route's parameters: named (`{ post }`, extra keys become the query string), one value (it fills the first
855
+ * parameter), or values in URI order (`[post, comment]`).
856
+ */
857
+ type RouteParameters<N extends string> = N extends keyof RouteList ? (GeneratedParameters<N> & Record<string, unknown>) | RouteParameter | readonly RouteParameter[] : unknown;
858
+ /**
859
+ * The arguments after a route name in route() and redirect().route(): the parameters, required when the route has a
860
+ * required one, then `Rest` (route()'s `absolute`, redirect().route()'s `status`). Before `oriel route:types` has
861
+ * named the route, anything is accepted.
862
+ */
863
+ type RouteArguments<N extends string, Rest extends unknown[] = [absolute?: boolean]> = N extends keyof RouteList ? [RequiredParameters<RouteList[N]>] extends [never] ? [parameters?: RouteParameters<N>, ...rest: Rest] : [parameters: RouteParameters<N>, ...rest: Rest] : [parameters?: unknown, ...rest: Rest];
437
864
  interface UrlGenerator {
438
865
  to(path: string, query?: Record<string, unknown>, absolute?: boolean): string;
439
866
  route(name: string, parameters?: unknown, absolute?: boolean): string;
@@ -452,16 +879,23 @@ declare const UrlGenerator: Token<UrlGenerator>;
452
879
  interface RedirectResponse extends Responsable {
453
880
  readonly targetUrl: string;
454
881
  with(key: string | Record<string, unknown>, value?: unknown): this;
455
- withErrors(errors: Record<string, string | string[]>, bag?: string): this;
882
+ /** Flashes the errors (a record, or a validator's `errors()`) into the bag, `default` unless named. */
883
+ withErrors(errors: Record<string, string | string[]> | MessageBag, bag?: string): this;
456
884
  withInput(input?: Record<string, unknown>): this;
457
885
  withCookie(name: string, value: string, minutes?: number): this;
886
+ header(name: string, value: string): this;
458
887
  }
459
888
  interface Redirector {
460
889
  to(path: string, status?: number): RedirectResponse;
461
890
  route(name: string, parameters?: unknown, status?: number): RedirectResponse;
462
891
  back(fallback?: string, status?: number): RedirectResponse;
463
- intended(fallback?: string): RedirectResponse;
892
+ intended(fallback?: string, status?: number): RedirectResponse;
464
893
  away(url: string, status?: number): RedirectResponse;
894
+ /** Remembers the URL a guest was going to (for intended()), then redirects to `path`. */
895
+ guest(path: string, status?: number): RedirectResponse;
896
+ /** Redirects to the current URL. */
897
+ refresh(status?: number): RedirectResponse;
898
+ setIntendedUrl(url: string): void;
465
899
  }
466
900
  declare const Redirector: Token<Redirector>;
467
901
 
@@ -540,6 +974,8 @@ interface Logger {
540
974
  interface LogManager extends Logger {
541
975
  channel(name?: string): Logger;
542
976
  stack(channels: string[]): Logger;
977
+ /** Writes out and closes the channels opened so far (log files are written asynchronously); a script awaits it last. */
978
+ flush(): Promise<void>;
543
979
  }
544
980
  declare const LogManager: Token<LogManager>;
545
981
 
@@ -630,6 +1066,8 @@ interface DatabaseConnection {
630
1066
  interface DatabaseManager extends DatabaseConnection {
631
1067
  connection(name?: string): DatabaseConnection;
632
1068
  disconnect(name?: string): Promise<void>;
1069
+ /** Closes every open connection and forgets them (the application's shutdown, the end of a test). */
1070
+ disconnectAll(): Promise<void>;
633
1071
  }
634
1072
  declare const DatabaseManager: Token<DatabaseManager>;
635
1073
  /** What non-Database components may rely on from a model instance. */
@@ -752,7 +1190,11 @@ interface ShouldQueue {
752
1190
  backoff?: number | number[];
753
1191
  /** Seconds. */
754
1192
  timeout?: number;
755
- uniqueId?(): string;
1193
+ /**
1194
+ * Laravel's ShouldBeUnique: while a job with this id is pending, another one is dropped. A queued listener's
1195
+ * receives the event (Laravel 13); a number is used as its string.
1196
+ */
1197
+ uniqueId?(...args: never[]): string | number;
756
1198
  }
757
1199
  /** A job: public fields are its serialized data; handle() runs on the worker (inject() works inside it). */
758
1200
  interface Job extends Partial<ShouldQueue> {
@@ -927,6 +1369,8 @@ interface ConsoleKernel {
927
1369
  command(signature: string, handler: (this: CommandContext, ...args: unknown[]) => number | void | Promise<number | void>): ClosureCommand;
928
1370
  }
929
1371
  declare const ConsoleKernel: Token<ConsoleKernel>;
1372
+ /** A callback a scheduled event runs before or after itself (before(), after(), onSuccess(), onFailure()). */
1373
+ type ScheduleHook = () => unknown;
930
1374
  interface ScheduledEvent {
931
1375
  readonly expression: string;
932
1376
  /**
@@ -939,24 +1383,69 @@ interface ScheduledEvent {
939
1383
  /** Minutes the withoutOverlapping() lock is kept at most (1440 unless withoutOverlapping() says). Laravel's `$event->expiresAt`. */
940
1384
  readonly expiresAt: number;
941
1385
  cron(expression: string): this;
1386
+ /**
1387
+ * Runs every N seconds: the event is due every minute, and schedule:run (or schedule:work) keeps running it until
1388
+ * the minute ends. N divides 60.
1389
+ */
1390
+ everySecond(): this;
1391
+ everyTwoSeconds(): this;
1392
+ everyFiveSeconds(): this;
1393
+ everyTenSeconds(): this;
1394
+ everyFifteenSeconds(): this;
1395
+ everyTwentySeconds(): this;
1396
+ everyThirtySeconds(): this;
942
1397
  everyMinute(): this;
1398
+ everyTwoMinutes(): this;
1399
+ everyThreeMinutes(): this;
1400
+ everyFourMinutes(): this;
943
1401
  everyFiveMinutes(): this;
1402
+ everyTenMinutes(): this;
944
1403
  everyFifteenMinutes(): this;
945
1404
  everyThirtyMinutes(): this;
946
1405
  hourly(): this;
947
- hourlyAt(minute: number): this;
1406
+ /** At these minutes past every hour. */
1407
+ hourlyAt(minute: number | readonly number[]): this;
1408
+ /** Every odd hour (1, 3, … 23), at `minute` past (0 by default). */
1409
+ everyOddHour(minute?: number | readonly number[]): this;
1410
+ everyTwoHours(minute?: number | readonly number[]): this;
1411
+ everyThreeHours(minute?: number | readonly number[]): this;
1412
+ everyFourHours(minute?: number | readonly number[]): this;
1413
+ everySixHours(minute?: number | readonly number[]): this;
948
1414
  daily(): this;
1415
+ /** The same as dailyAt(). */
1416
+ at(time: string): this;
949
1417
  dailyAt(time: string): this;
950
1418
  twiceDaily(first?: number, second?: number): this;
1419
+ /** At hours `first` and `second`, `minute` past. */
1420
+ twiceDailyAt(first?: number, second?: number, minute?: number): this;
1421
+ weekdays(): this;
1422
+ weekends(): this;
1423
+ mondays(): this;
1424
+ tuesdays(): this;
1425
+ wednesdays(): this;
1426
+ thursdays(): this;
1427
+ fridays(): this;
1428
+ saturdays(): this;
1429
+ sundays(): this;
951
1430
  weekly(): this;
952
- weeklyOn(day: number, time?: string): this;
1431
+ /** On these days of the week (0 = Sunday … 6 = Saturday), at `time` ('0:0' by default). */
1432
+ weeklyOn(day: number | readonly number[], time?: string): this;
953
1433
  monthly(): this;
954
- monthlyOn(day: number, time?: string): this;
1434
+ monthlyOn(day?: number, time?: string): this;
1435
+ twiceMonthly(first?: number, second?: number, time?: string): this;
1436
+ /** On the last day of every month, at `time`. */
1437
+ lastDayOfMonth(time?: string): this;
1438
+ quarterly(): this;
1439
+ /** On day `day` of the first month of each quarter, at `time`. */
1440
+ quarterlyOn(day?: number, time?: string): this;
955
1441
  yearly(): this;
956
- weekdays(): this;
957
- weekends(): this;
1442
+ yearlyOn(month?: number, day?: number, time?: string): this;
1443
+ /** On these days of the week (0 = Sunday … 6 = Saturday): `days([1, 3])` or `days(1, 3)`. */
1444
+ days(days: number | readonly number[], ...more: number[]): this;
958
1445
  timezone(timezone: string): this;
959
1446
  between(start: string, end: string): this;
1447
+ /** Skips the event between these times (a window may cross midnight: '23:00', '4:00'). */
1448
+ unlessBetween(start: string, end: string): this;
960
1449
  when(callback: () => boolean | Promise<boolean>): this;
961
1450
  skip(callback: () => boolean | Promise<boolean>): this;
962
1451
  /** Uses a cache lock (default 1440 minutes). */
@@ -966,6 +1455,15 @@ interface ScheduledEvent {
966
1455
  runInBackground(): this;
967
1456
  name(name: string): this;
968
1457
  description(description: string): this;
1458
+ /** Runs before the event, once its filters and locks let it run. */
1459
+ before(callback: ScheduleHook): this;
1460
+ /** Runs after the event, whether it succeeded or failed (also then()). */
1461
+ after(callback: ScheduleHook): this;
1462
+ then(callback: ScheduleHook): this;
1463
+ /** Runs after the event when it succeeded: a command exited 0, a closure neither threw nor returned false. */
1464
+ onSuccess(callback: ScheduleHook): this;
1465
+ /** Runs after the event when it failed: a command exited non-zero, a closure threw or returned false. */
1466
+ onFailure(callback: ScheduleHook): this;
969
1467
  /** The name, else the description: the command, the job's class, or 'Closure'. schedule:list shows it. */
970
1468
  getSummaryForDisplay(): string;
971
1469
  /**
@@ -1384,4 +1882,4 @@ interface LiveManager {
1384
1882
  }
1385
1883
  declare const LiveManager: Token<LiveManager>;
1386
1884
 
1387
- export { type AbilityCallback, type Abstract, type ActionMethods, type Address, Application, type Arrayable, type Attachment, AuthFactory, type Authenticatable, type AuthenticationError, type Batch, BatchRepository, type BodyInit, BusDispatcher, CacheManager, type CacheRepository, type Class, ClassRegistry, type ClosureCommand, type Command, type CommandContext, type Concrete, type ConfigCallbacks, ConfigRepository, ConsoleKernel, Container, type Content, type ControllerAction, type Cookie, CookieJar, type Credentials, CurrentRoute, type DatabaseConnection, DatabaseManager, type DatabaseNotification, type DriverName, Encrypter, type Enumerable, type Envelope, type ErrorKind, EventDispatcher, ExceptionCallbacks, ExceptionHandler, type ExceptionsConfigurator, type Factory, type FailedJob, FailedJobProvider, type Filesystem, FilesystemManager, Gate, type Guard, Hasher, type HeadersInit, type Htmlable, HttpKernel, HttpRequest, InertiaFactory, type InertiaOnceProp, type InertiaPage, type InertiaResponse, type InertiaScrollProp, InertiaSsrGateway, type InertiaSsrResponse, type InvokableClass, type Job, type Jsonable, type Listener, type ListenerClass, type LiveEffects, LiveManager, type LiveSnapshot, type LiveUpdateRequest, type LiveUpdateResponse, type Lock, type LogLevel, LogManager, type Logger, MailManager, type MailRecipient, type MailTransport, type Mailable, type Mailer, type MessageBag, type Middleware, MiddlewareCallbacks, type MiddlewareClass, type MiddlewareConfigurator, type MiddlewareReference, type ModelInstance, type ModelNotFoundError, type ModelStatic, type MustVerifyEmail, type Next, type Notifiable, type Notification, type NotificationChannel, NotificationDispatcher, type Operator, type OptionValue, type OrielError, type OutgoingMessage, PaginationState, PasswordBroker, type PasswordStatus, type PendingBatch, type PendingBatchOptions, type PendingChain, type PendingMail, type Pipe, type PipeClosure, type PipeNext, type Pipeline, PipelineFactory, type PropCallback, type PutOptions, type QueryBuilder, type Queue, type QueueJob, QueueManager, type QueuePayload, RateLimiter, type RedirectResponse, Redirector, type ResourceOptions, type Responsable, ResponseFactory, type Route, type RouteAction, type RouteClosure, type RouteList, RouteModelResolver, type RouteName, type RouteRegistrar, Router, RoutingPaths, type Row, type RuleCallback, type Rules, Schedule, type ScheduledEvent, type SentMessage, type ServiceProvider, type ServiceProviderClass, Session, type SessionHandler, type ShouldQueue, type StatefulGuard, type Token, type UploadedFile, UrlGenerator, type UserProvider, type ValidationError, ValidationFactory, type ValidationRule, type Validator, type View, ViewFactory, token };
1885
+ export { type AbilityCallback, type Abstract, type ActionMethods, type Address, Application, type Arrayable, type Attachment, AuthFactory, type Authenticatable, type AuthenticationError, type Batch, BatchRepository, type BodyInit, BusDispatcher, CacheManager, type CacheRepository, type Class, ClassRegistry, type ClosureCommand, type Command, type CommandContext, type Concrete, type ConfigCallbacks, ConfigRepository, ConsoleKernel, Container, type Content, type ControllerAction, type Cookie, CookieJar, type Credentials, CurrentRoute, type DatabaseConnection, DatabaseManager, type DatabaseNotification, type DriverName, Encrypter, type Enumerable, type Envelope, type ErrorKind, EventDispatcher, ExceptionCallbacks, ExceptionHandler, type ExceptionsConfigurator, type Factory, type FailedJob, FailedJobProvider, type Filesystem, FilesystemManager, Gate, type Guard, Hasher, type HeadersInit, type Htmlable, HttpKernel, HttpRequest, type InValue, InertiaFactory, type InertiaOnceProp, type InertiaPage, type InertiaResponse, type InertiaScrollProp, InertiaSsrGateway, type InertiaSsrResponse, type InferRuleType, type InferValidatedType, type InvokableClass, type Job, type Jsonable, type Listener, type ListenerClass, type LiveEffects, LiveManager, type LiveSnapshot, type LiveUpdateRequest, type LiveUpdateResponse, type Lock, type LogLevel, LogManager, type Logger, MailManager, type MailRecipient, type MailTransport, type Mailable, type Mailer, type MessageBag, type Middleware, MiddlewareCallbacks, type MiddlewareClass, type MiddlewareConfigurator, type MiddlewareReference, type ModelInstance, type ModelNotFoundError, type ModelStatic, type MustVerifyEmail, type Next, type Notifiable, type Notification, type NotificationChannel, NotificationDispatcher, type NumericValue, type Operator, type OptionValue, type OrielError, type OutgoingMessage, PaginationState, PasswordBroker, type PasswordStatus, type PendingBatch, type PendingBatchOptions, type PendingChain, type PendingMail, type Pipe, type PipeClosure, type PipeNext, type Pipeline, PipelineFactory, type PropCallback, type PutOptions, type QueryBuilder, type Queue, type QueueJob, QueueManager, type QueuePayload, RateLimiter, type RedirectResponse, Redirector, type ResourceOptions, type Responsable, ResponseFactory, type Route, type RouteAction, type RouteArguments, type RouteClosure, type RouteList, RouteModelResolver, type RouteName, type RouteParameter, type RouteParameters, type RouteRegistrar, Router, RoutingPaths, type Row, type RuleCallback, type RuleParameterList, type RuleTyping, type Rules, Schedule, type ScheduleHook, type ScheduledEvent, type SentMessage, type ServiceProvider, type ServiceProviderClass, Session, type SessionHandler, type ShouldQueue, type StatefulGuard, type Token, type TypedValidationRule, type UploadedFile, UrlGenerator, type UserProvider, type ValidationError, ValidationFactory, type ValidationRule, type ValidationRuleTypes, type Validator, type View, ViewFactory, token };