@orieljs/framework 0.1.0-alpha.5 → 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 (87) hide show
  1. package/NOTICE +7 -0
  2. package/README.md +10 -1
  3. package/TRADEMARKS.md +33 -0
  4. package/dist/{chunk-WNJP46B7.js → chunk-4MWG3B7G.js} +1785 -626
  5. package/dist/chunk-4MWG3B7G.js.map +1 -0
  6. package/dist/chunk-7ZQZYDOC.js +3253 -0
  7. package/dist/chunk-7ZQZYDOC.js.map +1 -0
  8. package/dist/{chunk-BSQKOLSK.js → chunk-GVNLZZTN.js} +1 -1
  9. package/dist/chunk-GVNLZZTN.js.map +1 -0
  10. package/dist/{chunk-A5XRLEWM.js → chunk-VOYW3R5J.js} +4 -4
  11. package/dist/chunk-VOYW3R5J.js.map +1 -0
  12. package/dist/contracts.d.ts +535 -17
  13. package/dist/contracts.js +1 -1
  14. package/dist/index.d-WQpfdIbG.d.ts +630 -0
  15. package/dist/index.d.ts +2202 -5952
  16. package/dist/index.js +4900 -944
  17. package/dist/index.js.map +1 -1
  18. package/dist/{jsx-runtime-WKTl3EEw.d.ts → jsx-runtime-6um4Mwfb.d.ts} +97 -51
  19. package/dist/jsx-runtime.d.ts +2 -2
  20. package/dist/jsx-runtime.js +3 -3
  21. package/dist/{testing-BErengYY.d.ts → testing-CKwfWJGn.d.ts} +313 -994
  22. package/dist/testing.d.ts +2 -2
  23. package/dist/testing.js +19 -5
  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-A5XRLEWM.js.map +0 -1
  83. package/dist/chunk-BSQKOLSK.js.map +0 -1
  84. package/dist/chunk-PM5YTG57.js +0 -880
  85. package/dist/chunk-PM5YTG57.js.map +0 -1
  86. package/dist/chunk-WNJP46B7.js.map +0 -1
  87. package/dist/index.d-DOlTeG-8.d.ts +0 -99
@@ -0,0 +1,255 @@
1
+ # Logging
2
+
3
+ Oriel writes your app's log through channels: a file, a file per day, or stderr. Each entry is one line of JSON,
4
+ written by pino.
5
+
6
+ ## Introduction
7
+
8
+ Logging is organized in channels, as in Laravel. Each channel writes entries one way: the `single` channel appends
9
+ them to one file, the `daily` channel to a file per day, and the `stderr` channel to the process's standard error.
10
+ A `stack` channel sends each entry to several channels at once, and each channel keeps only the entries at or above
11
+ its level.
12
+
13
+ The channels are written with pino, a Node logger, which writes each entry as a line of JSON.
14
+
15
+ ## Configuration
16
+
17
+ The channels are configured in `config/logging.ts`. A new app's file:
18
+
19
+ ```ts title="config/logging.ts"
20
+ import { dirname, join } from 'node:path'
21
+ import { env } from '@orieljs/framework'
22
+
23
+ const basePath = dirname(import.meta.dirname)
24
+ const path = join(basePath, 'storage/logs/oriel.log')
25
+ const level = env('LOG_LEVEL', 'debug')
26
+
27
+ export default {
28
+ default: env('LOG_CHANNEL', 'stack'),
29
+ channels: {
30
+ stack: { driver: 'stack', channels: String(env('LOG_STACK', 'single')).split(',') },
31
+ single: { driver: 'single', path, level },
32
+ daily: { driver: 'daily', path, level, days: Number(env('LOG_DAILY_DAYS', 14)) },
33
+ stderr: { driver: 'stderr', level },
34
+ mail: { driver: 'single', path: join(basePath, 'storage/logs/mail.log'), level },
35
+ null: { driver: 'null' },
36
+ },
37
+ }
38
+ ```
39
+
40
+ `default` is the channel your entries go to: `LOG_CHANNEL`, `stack` unless you set it. The `stack` channel holds the
41
+ channels `LOG_STACK` lists, comma-separated: `single` in a new app, which writes `storage/logs/oriel.log`. `LOG_LEVEL`
42
+ sets the level of every channel, `debug` by default, so every entry is kept. The `mail` channel is there for the [`log`
43
+ mailer](mail.md#configuration): with `MAIL_LOG_CHANNEL=mail` in your `.env`, the mail it would have sent goes to
44
+ `storage/logs/mail.log` rather than to the default channel.
45
+
46
+ A channel name that `channels` doesn't define is an error when it is used: "Log channel [name] is not defined in
47
+ config('logging.channels').".
48
+
49
+ ### Available channel drivers
50
+
51
+ | Name | What it does |
52
+ | --- | --- |
53
+ | `single` | Appends every entry to the file `path` names, and creates its folder if needed |
54
+ | `daily` | Writes to one file per day, `oriel-2026-10-19.log` for `oriel.log`, and keeps the files of the last `days` days (14 by default): each time it starts a day's file, it deletes older ones |
55
+ | `stderr` | Writes to the process's standard error, for a container or a process manager to collect |
56
+ | `stack` | Sends every entry to each of the channels it lists, in `channels` |
57
+ | `null` | Discards every entry |
58
+
59
+ Each channel but `stack` and `null` takes a `level`. A driver that isn't one of these is an error when its channel
60
+ is used, "Log driver [name] is not registered.".
61
+
62
+ > [!NOTE]
63
+ > Laravel's channels are Monolog handlers, with drivers for Slack, Papertrail, syslog and PHP's error log, and
64
+ > `custom` and `monolog` drivers for your own. Oriel writes with pino and has the five drivers above:
65
+ > on a server, `stderr` hands the log to whatever collects your process's output, which can forward it anywhere.
66
+
67
+ ## Building log stacks
68
+
69
+ The `stack` driver combines channels, so one entry goes to all of them. Here the stack writes every entry to the
70
+ daily files, and errors and worse to stderr too:
71
+
72
+ ```ts title="config/logging.ts"
73
+ import { dirname, join } from 'node:path'
74
+ import { env } from '@orieljs/framework'
75
+
76
+ const basePath = dirname(import.meta.dirname)
77
+ const path = join(basePath, 'storage/logs/oriel.log')
78
+
79
+ export default {
80
+ default: env('LOG_CHANNEL', 'stack'),
81
+ channels: {
82
+ stack: { driver: 'stack', channels: ['daily', 'stderr'] },
83
+ daily: { driver: 'daily', path, level: 'debug', days: 14 },
84
+ stderr: { driver: 'stderr', level: 'error' },
85
+ },
86
+ }
87
+ ```
88
+
89
+ A stack has no level of its own: each of its channels keeps the entries that meet its own level. A stack that lists
90
+ itself, directly or through another stack, is an error, "Log channel [stack] includes itself.".
91
+
92
+ #### Log levels
93
+
94
+ A channel's `level` is the least severe level it keeps. Oriel has the eight levels of RFC 5424, from the most severe:
95
+ `emergency`, `alert`, `critical`, `error`, `warning`, `notice`, `info` and `debug`. A channel at `warning` keeps
96
+ `warning`, `error`, `critical`, `alert` and `emergency` entries, and drops the others:
97
+
98
+ ```ts
99
+ Log.debug('An informational message.')
100
+
101
+ Log.emergency('The system is down!')
102
+ ```
103
+
104
+ With the stack above, the first entry is written to the daily file only, and the second to both channels. A level
105
+ that isn't one of the eight, such as pino's `warn`, is an error when the channel is made: "Invalid log level
106
+ [warn].".
107
+
108
+ ## Writing log messages
109
+
110
+ The `Log` facade writes to the default channel, with a method for each level:
111
+
112
+ ```ts
113
+ declare const message: string
114
+ // ---cut---
115
+ Log.emergency(message)
116
+ Log.alert(message)
117
+ Log.critical(message)
118
+ Log.error(message)
119
+ Log.warning(message)
120
+ Log.notice(message)
121
+ Log.info(message)
122
+ Log.debug(message)
123
+ ```
124
+
125
+ `Log.log('warning', message)` takes the level as its first argument. The `logger()` helper writes a `debug`
126
+ entry, `logger('Debug message')`, and with no arguments returns the same log manager as the facade.
127
+
128
+ A controller logs as any other code does:
129
+
130
+ ```ts title="app/Http/Controllers/UserController.ts"
131
+ import { Log, view } from '@orieljs/framework'
132
+ import { User } from '../../Models/User'
133
+ import { Controller } from './Controller'
134
+
135
+ export class UserController extends Controller {
136
+ /** Show the profile for the given user. */
137
+ async show(id: string) {
138
+ Log.info('Showing the user profile for user', { id })
139
+
140
+ return view('user.profile', { user: await User.findOrFail(id) })
141
+ }
142
+ }
143
+ ```
144
+
145
+ ### Contextual information
146
+
147
+ A method's second argument is the entry's context, an object written beside the message:
148
+
149
+ ```ts
150
+ declare const user: User
151
+ // ---cut---
152
+ Log.info('User failed to login.', { id: user.id })
153
+ ```
154
+
155
+ The message is written as it is. Laravel replaces placeholders such as `{id}` in the message with the context's
156
+ values; Oriel keeps the context in its own fields, where log tools can search them. An `Error` in the context is
157
+ written as its `name`, `message` and `stack`.
158
+
159
+ `Log.withContext()` adds context to every entry written after it in the same request, whatever the channel. A
160
+ middleware can give each request an id:
161
+
162
+ ```ts title="app/Http/Middleware/AssignRequestId.ts"
163
+ import { randomUUID } from 'node:crypto'
164
+ import { Log, withHeaders } from '@orieljs/framework'
165
+ import type { HttpRequest, Middleware, Next } from '@orieljs/framework/contracts'
166
+
167
+ export class AssignRequestId implements Middleware {
168
+ async handle(request: HttpRequest, next: Next): Promise<Response> {
169
+ const requestId = randomUUID()
170
+
171
+ Log.withContext({ 'request-id': requestId })
172
+
173
+ const response = await next(request)
174
+
175
+ return withHeaders(response, (headers) => headers.set('Request-Id', requestId))
176
+ }
177
+ }
178
+ ```
179
+
180
+ The context belongs to the request: two requests running at once keep their own, and it is gone when the request ends. A
181
+ console command and a queued job have a context of their own in the same way. Context added outside all of them, as in a
182
+ [service provider](providers.md), reaches none of them.
183
+
184
+ > [!NOTE]
185
+ > Laravel's `withContext()` adds context to the default channel, and `shareContext()` to every channel. Oriel keeps
186
+ > the context with the request, command or job rather than with a channel, so `withContext()` already reaches every
187
+ > channel, and there is no `shareContext()`.
188
+
189
+ ### Writing to specific channels
190
+
191
+ `Log.channel()` writes to a channel other than the default one:
192
+
193
+ ```ts
194
+ Log.channel('stderr').info('Something happened!')
195
+ ```
196
+
197
+ `Log.stack()` makes a stack of channels for one entry:
198
+
199
+ ```ts
200
+ Log.stack(['single', 'stderr']).info('Something happened!')
201
+ ```
202
+
203
+ Laravel's `Log::build()`, which makes a channel from a configuration given at run time, isn't in Oriel: add the
204
+ channel to `config/logging.ts`.
205
+
206
+ ## The log format
207
+
208
+ Each entry is one line of JSON, as pino writes it. This is a line of `storage/logs/oriel.log`, which your app writes
209
+ and you don't type, for the controller above:
210
+
211
+ ```json nocheck
212
+ {"level":30,"time":1792387488361,"pid":39221,"hostname":"web-1","id":"7","rfc5424Level":"info","msg":"Showing the user profile for user"}
213
+ ```
214
+
215
+ `msg` is the message and `rfc5424Level` its level. `time` is in milliseconds since 1970, and `pid` and `hostname`
216
+ tell the processes apart. The context's keys sit beside them, `id` here. `level` is pino's number for the level:
217
+ 20 for `debug`, 30 for `info` and `notice`, 40 for `warning`, 50 for `error`, and 60 for `critical`, `alert` and
218
+ `emergency`.
219
+
220
+ Reading the lines is up to the tools you use: `tail -f storage/logs/oriel.log` follows the file, and log services
221
+ read JSON lines as they are. Laravel Pail, which tails the log in the terminal, isn't in Oriel.
222
+
223
+ ## Flushing the log
224
+
225
+ pino writes in the background, so an entry may not be in the file yet when the line that logged it returns. Your app
226
+ writes out every waiting entry, and closes the files, when it shuts down: `node oriel` after its command finishes, and
227
+ the server when it receives `SIGINT` or `SIGTERM` (see [Deployment](deployment.md)). The log is closed after the
228
+ database, queue and cache connections, so an entry logged while they close is kept.
229
+
230
+ A script that makes the app itself must shut it down before it exits, or the entries still waiting are lost:
231
+
232
+ ```ts title="scripts/report.ts"
233
+ // @filename: bootstrap/app.ts
234
+ export declare function createApplication(): Promise<import('@orieljs/framework').Application>
235
+ // ---cut---
236
+ import { Log } from '@orieljs/framework'
237
+ import { createApplication } from '../bootstrap/app'
238
+
239
+ const app = await createApplication()
240
+
241
+ Log.info('Report started')
242
+
243
+ // ...
244
+
245
+ await app.shutdown()
246
+ ```
247
+
248
+ Calling `process.exit()` before `shutdown()` has finished loses them too. To write the waiting entries out without
249
+ shutting the app down, `await Log.flush()`. A [queued job](queues.md) or a command run by `node oriel` needs nothing:
250
+ the command shuts the app down when it ends.
251
+
252
+ ## Monolog channel customization
253
+
254
+ Oriel doesn't use Monolog, so Laravel's `tap`, Monolog handlers, formatters and processors, and channels made by
255
+ your own factories, aren't in Oriel.