@c9up/ream 0.2.7 → 0.2.8

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 (40) hide show
  1. package/dist/HttpKernel.d.ts.map +1 -1
  2. package/dist/HttpKernel.js +10 -0
  3. package/dist/HttpKernel.js.map +1 -1
  4. package/dist/debug.d.ts +22 -0
  5. package/dist/debug.d.ts.map +1 -0
  6. package/dist/debug.js +23 -0
  7. package/dist/debug.js.map +1 -0
  8. package/dist/dumper/Dumper.d.ts +80 -0
  9. package/dist/dumper/Dumper.d.ts.map +1 -0
  10. package/dist/dumper/Dumper.js +129 -0
  11. package/dist/dumper/Dumper.js.map +1 -0
  12. package/dist/index.d.ts +1 -1
  13. package/dist/index.js +1 -1
  14. package/dist/middleware/Pipeline.d.ts.map +1 -1
  15. package/dist/middleware/Pipeline.js +7 -0
  16. package/dist/middleware/Pipeline.js.map +1 -1
  17. package/dist/repl/Repl.d.ts +88 -0
  18. package/dist/repl/Repl.d.ts.map +1 -0
  19. package/dist/repl/Repl.js +123 -0
  20. package/dist/repl/Repl.js.map +1 -0
  21. package/dist/services/dumper.d.ts +25 -0
  22. package/dist/services/dumper.d.ts.map +1 -0
  23. package/dist/services/dumper.js +27 -0
  24. package/dist/services/dumper.js.map +1 -0
  25. package/dist/services/repl.d.ts +19 -0
  26. package/dist/services/repl.d.ts.map +1 -0
  27. package/dist/services/repl.js +19 -0
  28. package/dist/services/repl.js.map +1 -0
  29. package/events.win32-x64-msvc.node +0 -0
  30. package/index.win32-x64-msvc.node +0 -0
  31. package/package.json +17 -1
  32. package/scheduler.win32-x64-msvc.node +0 -0
  33. package/src/HttpKernel.ts +27 -0
  34. package/src/debug.ts +25 -0
  35. package/src/dumper/Dumper.ts +164 -0
  36. package/src/index.ts +1 -1
  37. package/src/middleware/Pipeline.ts +7 -0
  38. package/src/repl/Repl.ts +162 -0
  39. package/src/services/dumper.ts +30 -0
  40. package/src/services/repl.ts +21 -0
@@ -0,0 +1,162 @@
1
+ /**
2
+ * The application REPL — AdonisJS's `Repl`, in shape and in method names.
3
+ *
4
+ * import repl from '@c9up/ream/services/repl'
5
+ * repl.addMethod('loadModels', async () => { … }, {
6
+ * description: 'Load every entity onto the prompt',
7
+ * })
8
+ * repl.start({ app, router })
9
+ *
10
+ * Built on `node:repl`, which is the REPL — this adds what makes it an
11
+ * APPLICATION repl: a context seeded with the booted services, helpers a
12
+ * project registers itself, and `.ls` to list them, because a prompt full of
13
+ * undiscoverable helpers is a prompt nobody uses.
14
+ */
15
+
16
+ import type { REPLServer } from 'node:repl'
17
+ import { start as startNodeRepl } from 'node:repl'
18
+
19
+ /** What a registered helper receives — the repl itself, then its arguments. */
20
+ export type MethodCallback = (repl: Repl, ...args: unknown[]) => unknown
21
+
22
+ /** How a helper describes itself in `.ls`. */
23
+ export interface MethodOptions {
24
+ description?: string
25
+ usage?: string
26
+ }
27
+
28
+ /**
29
+ * A TypeScript compiler for the prompt, when the host has one.
30
+ *
31
+ * `node:repl` evaluates JavaScript. A project whose code is TypeScript wants
32
+ * to paste TypeScript, so the host supplies the compiler it already runs
33
+ * rather than ream carrying one.
34
+ */
35
+ export interface Compiler {
36
+ compile(code: string, fileName: string): string
37
+ supportsTypescript: boolean
38
+ }
39
+
40
+ interface RegisteredMethod {
41
+ handler: MethodCallback
42
+ options: MethodOptions & { width: number }
43
+ }
44
+
45
+ export class Repl {
46
+ readonly #methods = new Map<string, RegisteredMethod>()
47
+ readonly #readyCallbacks: Array<(repl: Repl) => void> = []
48
+ #compiler: Compiler | undefined
49
+ #server: REPLServer | undefined
50
+ readonly #prompt: string
51
+ readonly #write: (message: string) => void
52
+
53
+ constructor(options: { prompt?: string; write?: (message: string) => void } = {}) {
54
+ this.#prompt = options.prompt ?? '> '
55
+ this.#write =
56
+ options.write ??
57
+ ((message) => {
58
+ process.stdout.write(message)
59
+ })
60
+ }
61
+
62
+ /**
63
+ * Print a message around the prompt.
64
+ *
65
+ * Newline-wrapped: written mid-session the text would otherwise land on the
66
+ * line the user is typing.
67
+ */
68
+ notify(message: string): void {
69
+ this.#write(`\n${message}\n`)
70
+ }
71
+
72
+ /** Run `callback` once the prompt is up. Chainable. */
73
+ ready(callback: (repl: Repl) => void): this {
74
+ this.#readyCallbacks.push(callback)
75
+ return this
76
+ }
77
+
78
+ /**
79
+ * Register a helper, available at the prompt by name. Chainable.
80
+ *
81
+ * `width` is the name's display width, recorded here so `.ls` can align a
82
+ * column without measuring the set twice.
83
+ */
84
+ addMethod(name: string, handler: MethodCallback, options: MethodOptions = {}): this {
85
+ this.#methods.set(name, { handler, options: { ...options, width: name.length } })
86
+ return this
87
+ }
88
+
89
+ /** Every registered helper, keyed by name — AdonisJS `getMethods`. */
90
+ getMethods(): Record<string, RegisteredMethod> {
91
+ return Object.fromEntries(this.#methods)
92
+ }
93
+
94
+ /** Use `compiler` for the code typed at the prompt. Chainable. */
95
+ useCompiler(compiler: Compiler): this {
96
+ this.#compiler = compiler
97
+ return this
98
+ }
99
+
100
+ /** The compiler in use, if any. */
101
+ get compiler(): Compiler | undefined {
102
+ return this.#compiler
103
+ }
104
+
105
+ /** The underlying `node:repl` server, once started. */
106
+ get server(): REPLServer | undefined {
107
+ return this.#server
108
+ }
109
+
110
+ /**
111
+ * List the registered helpers, one per line, names aligned.
112
+ *
113
+ * Exposed rather than private because it IS the `.ls` command, and a caller
114
+ * driving the repl programmatically (a test, a custom command) needs the
115
+ * same listing without a live prompt.
116
+ */
117
+ describeMethods(): string {
118
+ if (this.#methods.size === 0) return 'No methods registered'
119
+ const widest = Math.max(...[...this.#methods.values()].map((m) => m.options.width))
120
+ return [...this.#methods]
121
+ .map(([name, method]) => {
122
+ const description = method.options.description ?? ''
123
+ const usage = method.options.usage ? ` (${method.options.usage})` : ''
124
+ return `${name.padEnd(widest)} ${description}${usage}`.trimEnd()
125
+ })
126
+ .join('\n')
127
+ }
128
+
129
+ /**
130
+ * Start the prompt, seeding it with `context`.
131
+ *
132
+ * Registered helpers are bound to this repl before the prompt appears — a
133
+ * helper that only exists after the first keystroke is a helper the user
134
+ * finds missing.
135
+ */
136
+ start(context: Record<string, unknown> = {}): this {
137
+ const server = startNodeRepl({ prompt: this.#prompt, useGlobal: false })
138
+ this.#server = server
139
+
140
+ for (const [key, value] of Object.entries(context)) {
141
+ Object.defineProperty(server.context, key, { value, configurable: true, enumerable: true })
142
+ }
143
+ for (const [name, method] of this.#methods) {
144
+ Object.defineProperty(server.context, name, {
145
+ value: (...args: unknown[]) => method.handler(this, ...args),
146
+ configurable: true,
147
+ enumerable: true,
148
+ })
149
+ }
150
+
151
+ server.defineCommand('ls', {
152
+ help: 'List the helpers available at this prompt',
153
+ action: () => {
154
+ this.notify(this.describeMethods())
155
+ server.displayPrompt()
156
+ },
157
+ })
158
+
159
+ for (const callback of this.#readyCallbacks) callback(this)
160
+ return this
161
+ }
162
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Dumper singleton service.
3
+ *
4
+ * import { dd } from '@c9up/ream/services/dumper'
5
+ * dd(user)
6
+ *
7
+ * import dumper from '@c9up/ream/services/dumper'
8
+ * dumper.configureAnsiOutput({ depth: 8 })
9
+ *
10
+ * Unlike the other accessors this one holds a real instance rather than a
11
+ * proxy: the dumper depends on nothing the boot phase builds, and something
12
+ * you reach for mid-debug should not require a booted application.
13
+ */
14
+
15
+ import { Dumper } from '../dumper/Dumper.js'
16
+
17
+ const dumper = new Dumper()
18
+
19
+ /**
20
+ * Dump and die — AdonisJS's `dd`.
21
+ *
22
+ * The trace index is 2, not the default 1: the frame above is this function,
23
+ * so the caller of `dd` is one further out. Without it, every dump would
24
+ * report this file as its source.
25
+ */
26
+ export const dd = (value: unknown): void => {
27
+ dumper.dd(value, 2)
28
+ }
29
+
30
+ export default dumper
@@ -0,0 +1,21 @@
1
+ /**
2
+ * REPL singleton service.
3
+ *
4
+ * // start/repl.ts, imported as a preload for the `repl` environment
5
+ * import repl from '@c9up/ream/services/repl'
6
+ * import { UserEntity } from '#app/entities/user'
7
+ *
8
+ * repl.addMethod('users', () => UserEntity, {
9
+ * description: 'The user entity, for querying at the prompt',
10
+ * })
11
+ *
12
+ * Holds a real instance rather than a proxy: helpers are registered BEFORE the
13
+ * prompt starts — that is the point of registering them — so the object has to
14
+ * exist before anything boots it.
15
+ */
16
+
17
+ import { Repl } from '../repl/Repl.js'
18
+
19
+ const repl = new Repl()
20
+
21
+ export default repl