@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.
- package/dist/HttpKernel.d.ts.map +1 -1
- package/dist/HttpKernel.js +10 -0
- package/dist/HttpKernel.js.map +1 -1
- package/dist/debug.d.ts +22 -0
- package/dist/debug.d.ts.map +1 -0
- package/dist/debug.js +23 -0
- package/dist/debug.js.map +1 -0
- package/dist/dumper/Dumper.d.ts +80 -0
- package/dist/dumper/Dumper.d.ts.map +1 -0
- package/dist/dumper/Dumper.js +129 -0
- package/dist/dumper/Dumper.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/middleware/Pipeline.d.ts.map +1 -1
- package/dist/middleware/Pipeline.js +7 -0
- package/dist/middleware/Pipeline.js.map +1 -1
- package/dist/repl/Repl.d.ts +88 -0
- package/dist/repl/Repl.d.ts.map +1 -0
- package/dist/repl/Repl.js +123 -0
- package/dist/repl/Repl.js.map +1 -0
- package/dist/services/dumper.d.ts +25 -0
- package/dist/services/dumper.d.ts.map +1 -0
- package/dist/services/dumper.js +27 -0
- package/dist/services/dumper.js.map +1 -0
- package/dist/services/repl.d.ts +19 -0
- package/dist/services/repl.d.ts.map +1 -0
- package/dist/services/repl.js +19 -0
- package/dist/services/repl.js.map +1 -0
- package/events.win32-x64-msvc.node +0 -0
- package/index.win32-x64-msvc.node +0 -0
- package/package.json +17 -1
- package/scheduler.win32-x64-msvc.node +0 -0
- package/src/HttpKernel.ts +27 -0
- package/src/debug.ts +25 -0
- package/src/dumper/Dumper.ts +164 -0
- package/src/index.ts +1 -1
- package/src/middleware/Pipeline.ts +7 -0
- package/src/repl/Repl.ts +162 -0
- package/src/services/dumper.ts +30 -0
- package/src/services/repl.ts +21 -0
package/src/repl/Repl.ts
ADDED
|
@@ -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
|