@quazardous/qdadm 2.32.0 → 2.33.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.
- package/dist/styles/qdadm.css +1 -1
- package/package.json +8 -2
- package/src/components/display/EmailPreview.vue +227 -0
- package/src/components/display/EntityTimeline.vue +408 -0
- package/src/components/display/LogStream.vue +364 -0
- package/src/components/display/PayloadDiff.vue +236 -0
- package/src/components/display/PayloadViewer.vue +246 -0
- package/src/components/index.ts +8 -0
- package/src/components/layout/AppLayout.vue +8 -0
- package/src/components/tasks/TaskDialog.vue +95 -0
- package/src/components/tasks/TaskHost.vue +45 -0
- package/src/components/tasks/TaskTray.vue +54 -0
- package/src/composables/index.ts +11 -0
- package/src/composables/useListPage.filters.ts +26 -1
- package/src/composables/useLogStream.ts +264 -0
- package/src/email/index.ts +46 -0
- package/src/entity/storage/SdkStorage.ts +53 -1
- package/src/entity/storage/index.ts +2 -0
- package/src/i18n/defaults/core.en.generated.ts +85 -0
- package/src/i18n/defaults/core.en.yml +85 -0
- package/src/i18n/defaults/core.fr.generated.ts +85 -0
- package/src/i18n/defaults/core.fr.yml +85 -0
- package/src/index.ts +21 -0
- package/src/kernel/Kernel.vue.ts +9 -2
- package/src/styles/_email.scss +188 -0
- package/src/styles/_log-stream.scss +167 -0
- package/src/styles/_payload.scss +312 -0
- package/src/styles/_tasks.scss +212 -0
- package/src/styles/_timeline.scss +229 -0
- package/src/styles/index.scss +5 -0
- package/src/tasks/taskStore.ts +460 -0
- package/src/tasks/useTaskRunner.ts +52 -0
- package/src/utils/ansi.ts +127 -0
- package/src/utils/email.ts +270 -0
- package/src/utils/index.ts +32 -0
- package/src/utils/payload.ts +480 -0
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useLogStream - the line buffer behind LogStream (#4110)
|
|
3
|
+
*
|
|
4
|
+
* Reads a backend log as it is written, from one of four sources the app
|
|
5
|
+
* picks, and keeps the lines parsed once (ANSI styles, error flag) so the
|
|
6
|
+
* view only slices them.
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* // Offsets: GET /tasks/42/log?from=<n> → { lines, next, done }
|
|
10
|
+
* useLogStream({ poll: (from) => api.get(`/tasks/42/log`, { params: { from } }).then((r) => r.data) })
|
|
11
|
+
*
|
|
12
|
+
* // Server-sent events, one line (or several, newline-separated) per message
|
|
13
|
+
* useLogStream({ sse: '/tasks/42/log/stream', endEvent: 'end' })
|
|
14
|
+
*
|
|
15
|
+
* // Anything that pushes: an SSEBridge event, a WebSocket…
|
|
16
|
+
* useLogStream({ subscribe: ({ push, end }) => bridge.onEvent('task:log', (p) => push(p.data.lines)) })
|
|
17
|
+
*
|
|
18
|
+
* // A finished log
|
|
19
|
+
* useLogStream({ lines: text.split('\n') })
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { shallowRef, ref, onScopeDispose, getCurrentScope, type Ref, type ShallowRef } from 'vue'
|
|
24
|
+
import { parseAnsiLine, stripAnsi, type AnsiSegment, type AnsiStyle } from '../utils/ansi'
|
|
25
|
+
|
|
26
|
+
export interface LogPollResult {
|
|
27
|
+
lines: string[]
|
|
28
|
+
/** Offset to ask for next. Defaults to the previous one plus the lines received. */
|
|
29
|
+
next?: number
|
|
30
|
+
/** True once the log is complete: polling stops. */
|
|
31
|
+
done?: boolean
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface LogPushApi {
|
|
35
|
+
push: (lines: string | string[]) => void
|
|
36
|
+
end: () => void
|
|
37
|
+
fail: (error: unknown) => void
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type LogSource =
|
|
41
|
+
| { poll: (from: number) => Promise<LogPollResult>; intervalMs?: number; from?: number }
|
|
42
|
+
| {
|
|
43
|
+
sse: string | (() => EventSource)
|
|
44
|
+
/** Event carrying lines. Default `message`. */
|
|
45
|
+
event?: string
|
|
46
|
+
/** Event that closes the log. Default `end`. */
|
|
47
|
+
endEvent?: string
|
|
48
|
+
/** Turn a message's data into lines. Default: JSON `{ lines, done }` or plain text split on newlines. */
|
|
49
|
+
parse?: (data: string) => string[] | LogPollResult
|
|
50
|
+
}
|
|
51
|
+
| { subscribe: (api: LogPushApi) => (() => void) | void }
|
|
52
|
+
| { lines: string[] }
|
|
53
|
+
|
|
54
|
+
export interface LogLine {
|
|
55
|
+
/** 1-based number in the whole log, kept when old lines are dropped. */
|
|
56
|
+
n: number
|
|
57
|
+
raw: string
|
|
58
|
+
/** The line without ANSI escapes: what search, errors and download read. */
|
|
59
|
+
text: string
|
|
60
|
+
segments: AnsiSegment[]
|
|
61
|
+
error: boolean
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export type LogStreamStatus = 'idle' | 'streaming' | 'done' | 'error'
|
|
65
|
+
|
|
66
|
+
export interface UseLogStreamOptions {
|
|
67
|
+
/** Lines matching it are flagged as errors. */
|
|
68
|
+
errorPattern?: RegExp | string | null
|
|
69
|
+
/** Oldest lines are dropped past this count. Default 100 000. */
|
|
70
|
+
maxLines?: number
|
|
71
|
+
/** Start reading at once. Default true. */
|
|
72
|
+
immediate?: boolean
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface UseLogStreamReturn {
|
|
76
|
+
lines: ShallowRef<LogLine[]>
|
|
77
|
+
status: Ref<LogStreamStatus>
|
|
78
|
+
error: Ref<string | null>
|
|
79
|
+
/** Lines dropped from the start because of maxLines. */
|
|
80
|
+
dropped: Ref<number>
|
|
81
|
+
start: () => void
|
|
82
|
+
stop: () => void
|
|
83
|
+
/** Empty the buffer and read the source again from its start. */
|
|
84
|
+
restart: () => void
|
|
85
|
+
/** Add lines by hand (used by the push sources, open to the app). */
|
|
86
|
+
append: (lines: string | string[]) => void
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const MAX_BACKOFF_MS = 30_000
|
|
90
|
+
|
|
91
|
+
function toRegExp(pattern: RegExp | string | null | undefined): RegExp | null {
|
|
92
|
+
if (!pattern) return null
|
|
93
|
+
if (typeof pattern === 'string') return new RegExp(pattern, 'i')
|
|
94
|
+
// A global or sticky regex keeps lastIndex between test() calls and skips lines.
|
|
95
|
+
return new RegExp(pattern.source, pattern.flags.replace(/[gy]/g, ''))
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function splitLines(chunk: string | string[]): string[] {
|
|
99
|
+
const list = Array.isArray(chunk) ? chunk : [chunk]
|
|
100
|
+
return list.flatMap((l) => String(l).replace(/\r\n?/g, '\n').split('\n'))
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function defaultParse(data: string): string[] | LogPollResult {
|
|
104
|
+
const trimmed = data.trimStart()
|
|
105
|
+
if (trimmed.startsWith('{')) {
|
|
106
|
+
try {
|
|
107
|
+
const parsed = JSON.parse(data)
|
|
108
|
+
if (parsed && Array.isArray(parsed.lines)) return parsed as LogPollResult
|
|
109
|
+
} catch {
|
|
110
|
+
// not JSON: a line that happens to start with a brace
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return splitLines(data)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function useLogStream(source: LogSource, options: UseLogStreamOptions = {}): UseLogStreamReturn {
|
|
117
|
+
const { maxLines = 100_000, immediate = true } = options
|
|
118
|
+
const errorRe = toRegExp(options.errorPattern)
|
|
119
|
+
|
|
120
|
+
const lines = shallowRef<LogLine[]>([])
|
|
121
|
+
const status = ref<LogStreamStatus>('idle')
|
|
122
|
+
const error = ref<string | null>(null)
|
|
123
|
+
const dropped = ref(0)
|
|
124
|
+
|
|
125
|
+
let ansiState: AnsiStyle = {}
|
|
126
|
+
let counter = 0
|
|
127
|
+
let teardown: (() => void) | null = null
|
|
128
|
+
// A restart must not let a poll answer from the previous run append.
|
|
129
|
+
let run = 0
|
|
130
|
+
|
|
131
|
+
function append(chunk: string | string[]): void {
|
|
132
|
+
const incoming = splitLines(chunk)
|
|
133
|
+
if (incoming.length === 0) return
|
|
134
|
+
const parsed: LogLine[] = incoming.map((raw) => {
|
|
135
|
+
const { segments, state } = parseAnsiLine(raw, ansiState)
|
|
136
|
+
ansiState = state
|
|
137
|
+
const text = stripAnsi(raw)
|
|
138
|
+
return { n: ++counter, raw, text, segments, error: errorRe ? errorRe.test(text) : false }
|
|
139
|
+
})
|
|
140
|
+
let next = lines.value.concat(parsed)
|
|
141
|
+
if (next.length > maxLines) {
|
|
142
|
+
dropped.value += next.length - maxLines
|
|
143
|
+
next = next.slice(next.length - maxLines)
|
|
144
|
+
}
|
|
145
|
+
lines.value = next
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function finish(): void {
|
|
149
|
+
status.value = 'done'
|
|
150
|
+
teardown?.()
|
|
151
|
+
teardown = null
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function fail(err: unknown): void {
|
|
155
|
+
error.value = err instanceof Error ? err.message : String(err)
|
|
156
|
+
status.value = 'error'
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function startPoll(src: Extract<LogSource, { poll: unknown }>): void {
|
|
160
|
+
const interval = src.intervalMs ?? 1000
|
|
161
|
+
const current = run
|
|
162
|
+
let offset = src.from ?? 0
|
|
163
|
+
let delay = interval
|
|
164
|
+
let timer: ReturnType<typeof setTimeout> | null = null
|
|
165
|
+
let stopped = false
|
|
166
|
+
|
|
167
|
+
const tick = async () => {
|
|
168
|
+
if (stopped || current !== run) return
|
|
169
|
+
try {
|
|
170
|
+
const result = await src.poll(offset)
|
|
171
|
+
if (stopped || current !== run) return
|
|
172
|
+
const received = result?.lines ?? []
|
|
173
|
+
append(received)
|
|
174
|
+
offset = result?.next ?? offset + received.length
|
|
175
|
+
status.value = 'streaming'
|
|
176
|
+
error.value = null
|
|
177
|
+
delay = interval
|
|
178
|
+
if (result?.done) return finish()
|
|
179
|
+
} catch (err) {
|
|
180
|
+
if (stopped || current !== run) return
|
|
181
|
+
fail(err)
|
|
182
|
+
delay = Math.min(delay * 2, MAX_BACKOFF_MS)
|
|
183
|
+
}
|
|
184
|
+
timer = setTimeout(tick, delay)
|
|
185
|
+
}
|
|
186
|
+
teardown = () => {
|
|
187
|
+
stopped = true
|
|
188
|
+
if (timer) clearTimeout(timer)
|
|
189
|
+
}
|
|
190
|
+
void tick()
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function startSse(src: Extract<LogSource, { sse: unknown }>): void {
|
|
194
|
+
const es = typeof src.sse === 'string' ? new EventSource(src.sse) : src.sse()
|
|
195
|
+
const parse = src.parse ?? defaultParse
|
|
196
|
+
const onLines = (event: Event) => {
|
|
197
|
+
const result = parse(String((event as MessageEvent).data ?? ''))
|
|
198
|
+
if (Array.isArray(result)) append(result)
|
|
199
|
+
else {
|
|
200
|
+
append(result.lines ?? [])
|
|
201
|
+
if (result.done) finish()
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
const onEnd = () => finish()
|
|
205
|
+
const onError = () => {
|
|
206
|
+
// EventSource reconnects by itself; only a closed source is a failure.
|
|
207
|
+
if (es.readyState === 2) fail('The log stream closed')
|
|
208
|
+
}
|
|
209
|
+
es.addEventListener(src.event ?? 'message', onLines)
|
|
210
|
+
es.addEventListener(src.endEvent ?? 'end', onEnd)
|
|
211
|
+
es.addEventListener('error', onError)
|
|
212
|
+
teardown = () => es.close()
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function startSubscribe(src: Extract<LogSource, { subscribe: unknown }>): void {
|
|
216
|
+
const current = run
|
|
217
|
+
const guard = <A extends unknown[]>(fn: (...args: A) => void) => (...args: A) => {
|
|
218
|
+
if (current === run && status.value === 'streaming') fn(...args)
|
|
219
|
+
}
|
|
220
|
+
const unsubscribe = src.subscribe({ push: guard(append), end: guard(finish), fail: guard(fail) })
|
|
221
|
+
if (status.value === 'streaming' && current === run) {
|
|
222
|
+
teardown = () => unsubscribe?.()
|
|
223
|
+
} else {
|
|
224
|
+
unsubscribe?.()
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function start(): void {
|
|
229
|
+
if (teardown || status.value === 'streaming' || status.value === 'done') return
|
|
230
|
+
run++
|
|
231
|
+
error.value = null
|
|
232
|
+
if ('lines' in source) {
|
|
233
|
+
append(source.lines)
|
|
234
|
+
status.value = 'done'
|
|
235
|
+
return
|
|
236
|
+
}
|
|
237
|
+
status.value = 'streaming'
|
|
238
|
+
if ('poll' in source) startPoll(source)
|
|
239
|
+
else if ('sse' in source) startSse(source)
|
|
240
|
+
else startSubscribe(source)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function stop(): void {
|
|
244
|
+
run++
|
|
245
|
+
teardown?.()
|
|
246
|
+
teardown = null
|
|
247
|
+
if (status.value === 'streaming') status.value = 'idle'
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function restart(): void {
|
|
251
|
+
stop()
|
|
252
|
+
lines.value = []
|
|
253
|
+
dropped.value = 0
|
|
254
|
+
counter = 0
|
|
255
|
+
ansiState = {}
|
|
256
|
+
status.value = 'idle'
|
|
257
|
+
start()
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (getCurrentScope()) onScopeDispose(stop)
|
|
261
|
+
if (immediate) start()
|
|
262
|
+
|
|
263
|
+
return { lines, status, error, dropped, start, stop, restart, append }
|
|
264
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* qdadm/email - raw MIME parsing for EmailPreview (#4113), through postal-mime
|
|
3
|
+
*
|
|
4
|
+
* Import from '@quazardous/qdadm/email' only if the app shows raw MIME
|
|
5
|
+
* messages (an .eml, a sink that returns the source). postal-mime is an
|
|
6
|
+
* optional peer dependency: install it to use this entry point. A parsed
|
|
7
|
+
* email ({ headers, html, text, attachments }) needs neither.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* import { parseMime } from '@quazardous/qdadm/email'
|
|
11
|
+
* <EmailPreview :raw="capture.source" :parser="parseMime" />
|
|
12
|
+
*/
|
|
13
|
+
import PostalMime from 'postal-mime'
|
|
14
|
+
import type { EmailAddress, ParsedEmail } from '../utils/email'
|
|
15
|
+
|
|
16
|
+
type PostalAddress = { name: string; address?: string; group?: Array<{ name: string; address: string }> }
|
|
17
|
+
|
|
18
|
+
function flatten(list: PostalAddress[] | PostalAddress | undefined): EmailAddress[] {
|
|
19
|
+
const items = !list ? [] : Array.isArray(list) ? list : [list]
|
|
20
|
+
return items.flatMap((a): Array<{ name: string; address?: string }> => (a.group ? a.group : [a])).map((a) => ({ name: a.name || null, address: a.address ?? null }))
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Parse a raw MIME message. The source is kept for the Source tab. */
|
|
24
|
+
export async function parseMime(raw: string | ArrayBuffer | Uint8Array): Promise<ParsedEmail> {
|
|
25
|
+
const email = await new PostalMime({ attachmentEncoding: 'base64' }).parse(raw)
|
|
26
|
+
return {
|
|
27
|
+
from: flatten(email.from as PostalAddress | undefined)[0] ?? null,
|
|
28
|
+
to: flatten(email.to as PostalAddress[] | undefined),
|
|
29
|
+
cc: flatten(email.cc as PostalAddress[] | undefined),
|
|
30
|
+
replyTo: flatten(email.replyTo as PostalAddress[] | undefined),
|
|
31
|
+
subject: email.subject ?? null,
|
|
32
|
+
date: email.date ?? null,
|
|
33
|
+
messageId: email.messageId ?? null,
|
|
34
|
+
headers: email.headers.map((h) => ({ key: h.originalKey ?? h.key, value: h.value })),
|
|
35
|
+
html: email.html ?? null,
|
|
36
|
+
text: email.text ?? null,
|
|
37
|
+
raw: typeof raw === 'string' ? raw : new TextDecoder().decode(raw),
|
|
38
|
+
attachments: email.attachments.map((a) => ({
|
|
39
|
+
filename: a.filename,
|
|
40
|
+
contentType: a.mimeType,
|
|
41
|
+
content: a.content as string,
|
|
42
|
+
contentId: a.contentId ?? null,
|
|
43
|
+
disposition: a.disposition,
|
|
44
|
+
})),
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -62,6 +62,29 @@ export interface SdkMethods<T extends EntityRecord = EntityRecord> {
|
|
|
62
62
|
[key: string]: SdkMethodConfig<T> | undefined
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
/** The options of a path request, as `EntityManager.request` passes them. */
|
|
66
|
+
export interface SdkPathRequestOptions {
|
|
67
|
+
data?: unknown
|
|
68
|
+
params?: Record<string, unknown>
|
|
69
|
+
headers?: Record<string, string>
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* How a path request (`'GET', 'distinct/status'`) reaches the backend (#4069).
|
|
74
|
+
*
|
|
75
|
+
* A generated SDK has no method for an arbitrary path, and no reliable way
|
|
76
|
+
* to tell its base URL or this entity's endpoint, so the app supplies it —
|
|
77
|
+
* usually through the SDK's own client.
|
|
78
|
+
*/
|
|
79
|
+
export type SdkPathRequest = (
|
|
80
|
+
method: string,
|
|
81
|
+
path: string,
|
|
82
|
+
options: SdkPathRequestOptions,
|
|
83
|
+
sdk: SdkInstance
|
|
84
|
+
) => Promise<unknown>
|
|
85
|
+
|
|
86
|
+
const HTTP_METHODS = new Set(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'])
|
|
87
|
+
|
|
65
88
|
/**
|
|
66
89
|
* SdkStorage options
|
|
67
90
|
*/
|
|
@@ -74,6 +97,8 @@ export interface SdkStorageOptions<T extends EntityRecord = EntityRecord> {
|
|
|
74
97
|
transforms?: Partial<Record<string, SdkMethodTransforms<T>>>
|
|
75
98
|
responseFormat?: SdkResponseFormat | null
|
|
76
99
|
clientSidePagination?: boolean
|
|
100
|
+
/** Runs `request(method, path, options)` calls — `optionsEndpoint` filters among them. */
|
|
101
|
+
request?: SdkPathRequest | null
|
|
77
102
|
}
|
|
78
103
|
|
|
79
104
|
/**
|
|
@@ -101,6 +126,7 @@ export class SdkStorage<T extends EntityRecord = EntityRecord> extends IStorage<
|
|
|
101
126
|
protected _transformResponse: SdkGlobalResponseTransform<T> | null
|
|
102
127
|
protected _transforms: Partial<Record<string, SdkMethodTransforms<T>>>
|
|
103
128
|
protected _responseFormat: SdkResponseFormat | null
|
|
129
|
+
protected _pathRequest: SdkPathRequest | null
|
|
104
130
|
|
|
105
131
|
constructor(options: SdkStorageOptions<T> = {}) {
|
|
106
132
|
super()
|
|
@@ -113,6 +139,7 @@ export class SdkStorage<T extends EntityRecord = EntityRecord> extends IStorage<
|
|
|
113
139
|
transforms = {},
|
|
114
140
|
responseFormat = null,
|
|
115
141
|
clientSidePagination = false,
|
|
142
|
+
request = null,
|
|
116
143
|
} = options
|
|
117
144
|
|
|
118
145
|
this._sdk = sdk
|
|
@@ -123,6 +150,12 @@ export class SdkStorage<T extends EntityRecord = EntityRecord> extends IStorage<
|
|
|
123
150
|
this._transforms = transforms
|
|
124
151
|
this._responseFormat = responseFormat
|
|
125
152
|
this.clientSidePagination = clientSidePagination
|
|
153
|
+
this._pathRequest = request
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** Whether `request(method, path)` can run, i.e. a `request` option was given. */
|
|
157
|
+
get supportsPathRequests(): boolean {
|
|
158
|
+
return this._pathRequest !== null
|
|
126
159
|
}
|
|
127
160
|
|
|
128
161
|
get sdk(): SdkInstance {
|
|
@@ -335,7 +368,26 @@ export class SdkStorage<T extends EntityRecord = EntityRecord> extends IStorage<
|
|
|
335
368
|
await this._execute('delete', pathParams)
|
|
336
369
|
}
|
|
337
370
|
|
|
338
|
-
|
|
371
|
+
/**
|
|
372
|
+
* Two calls share this name. `request('GET', 'distinct/status', options)` is
|
|
373
|
+
* the path request every storage answers, the one `EntityManager.request`
|
|
374
|
+
* makes; it goes through the `request` option. `request('getStats', params)`
|
|
375
|
+
* calls a configured method or an SDK method by name.
|
|
376
|
+
*
|
|
377
|
+
* Told apart by the verb: no SDK method is called `GET`, and before #4069
|
|
378
|
+
* the path form looked one up and failed with "SDK method 'GET' not found".
|
|
379
|
+
*/
|
|
380
|
+
async request<R = unknown>(methodName: string, params: unknown = {}, options: SdkPathRequestOptions = {}): Promise<R> {
|
|
381
|
+
if (HTTP_METHODS.has(methodName) && typeof params === 'string') {
|
|
382
|
+
if (!this._pathRequest) {
|
|
383
|
+
throw new Error(
|
|
384
|
+
`SdkStorage cannot run ${methodName} '${params}': a generated SDK has no method for a path. ` +
|
|
385
|
+
`Pass a request option, e.g. request: (method, url, o, sdk) => sdk.client.request({ method, url, query: o.params, body: o.data })`
|
|
386
|
+
)
|
|
387
|
+
}
|
|
388
|
+
return (await this._pathRequest(methodName, params, options, this.sdk)) as R
|
|
389
|
+
}
|
|
390
|
+
|
|
339
391
|
if (this._methods[methodName]) {
|
|
340
392
|
return (await this._execute(methodName, params)) as R
|
|
341
393
|
}
|
|
@@ -71,6 +71,91 @@ export default `core:
|
|
|
71
71
|
notRegistered: not registered
|
|
72
72
|
remove: Remove
|
|
73
73
|
via: "via {origin}"
|
|
74
|
+
# LogStream (#4110)
|
|
75
|
+
log:
|
|
76
|
+
streaming: Streaming
|
|
77
|
+
done: Complete
|
|
78
|
+
stopped: Stopped
|
|
79
|
+
failed: "Failed: {error}"
|
|
80
|
+
lines: "{count} lines"
|
|
81
|
+
search: Search the log
|
|
82
|
+
previous: Previous match
|
|
83
|
+
next: Next match
|
|
84
|
+
onlyMatches: Only matching lines
|
|
85
|
+
errors: "{count} errors"
|
|
86
|
+
firstError: Go to the next error line
|
|
87
|
+
wrap: Wrap long lines
|
|
88
|
+
follow: Follow the end of the log
|
|
89
|
+
download: Download the log
|
|
90
|
+
newLines: "{count} new lines, resume"
|
|
91
|
+
empty: No output yet
|
|
92
|
+
# EntityTimeline (#4111)
|
|
93
|
+
timeline:
|
|
94
|
+
actors: Filter by actor
|
|
95
|
+
loading: Loading…
|
|
96
|
+
empty: Nothing happened yet
|
|
97
|
+
newEvents: "{count} new events"
|
|
98
|
+
oldestFirst: Oldest first
|
|
99
|
+
newestFirst: Newest first
|
|
100
|
+
# PayloadViewer / PayloadDiff (#4112)
|
|
101
|
+
payload:
|
|
102
|
+
headers: "Headers ({count})"
|
|
103
|
+
headerRow: First row is a header
|
|
104
|
+
copy: Copy
|
|
105
|
+
copied: Copied to the clipboard
|
|
106
|
+
copyFailed: Could not copy
|
|
107
|
+
download: Download
|
|
108
|
+
showAll: "Show all {count} rows"
|
|
109
|
+
binary: "Binary content, {size}. First bytes:"
|
|
110
|
+
malformed: "Not valid {format} ({error}), shown as text"
|
|
111
|
+
expected: Expected
|
|
112
|
+
captured: Captured
|
|
113
|
+
identical: Identical
|
|
114
|
+
unchanged: "{count} unchanged lines"
|
|
115
|
+
showSame: "Show {count} unchanged rows"
|
|
116
|
+
inline: Inline
|
|
117
|
+
split: Side by side
|
|
118
|
+
# EmailPreview (#4113)
|
|
119
|
+
email:
|
|
120
|
+
from: From
|
|
121
|
+
to: To
|
|
122
|
+
cc: Cc
|
|
123
|
+
replyTo: Reply to
|
|
124
|
+
date: Date
|
|
125
|
+
noSubject: (no subject)
|
|
126
|
+
allHeaders: "All headers ({count})"
|
|
127
|
+
noParser: "A raw message needs a parser: pass parseMime from @quazardous/qdadm/email. Showing the source."
|
|
128
|
+
tab:
|
|
129
|
+
html: HTML
|
|
130
|
+
text: Text
|
|
131
|
+
source: Source
|
|
132
|
+
imagesBlocked: "{count} remote images blocked"
|
|
133
|
+
loadImages: Load images
|
|
134
|
+
blockImages: Block images
|
|
135
|
+
empty: This email has no body
|
|
136
|
+
unnamed: (unnamed)
|
|
137
|
+
inline: inline
|
|
138
|
+
download: Download
|
|
139
|
+
# TaskRunner (#4114)
|
|
140
|
+
task:
|
|
141
|
+
status:
|
|
142
|
+
starting: Starting
|
|
143
|
+
queued: Queued
|
|
144
|
+
running: Running
|
|
145
|
+
succeeded: Done
|
|
146
|
+
failed: Failed
|
|
147
|
+
cancelled: Cancelled
|
|
148
|
+
cancel: Cancel the task
|
|
149
|
+
close: Close
|
|
150
|
+
background: Run in the background
|
|
151
|
+
open: Open the task
|
|
152
|
+
readError: "Could not read the task ({error}), still trying"
|
|
153
|
+
start: Start
|
|
154
|
+
dontStart: Do not start
|
|
155
|
+
typeToConfirm: "Type {value} to confirm"
|
|
156
|
+
tray: "{count} running tasks"
|
|
157
|
+
trayDone: Recent tasks
|
|
158
|
+
clear: Clear finished
|
|
74
159
|
# The breadcrumb's View↔Edit toggle (useNavContext).
|
|
75
160
|
breadcrumb:
|
|
76
161
|
view: View
|
|
@@ -69,6 +69,91 @@ core:
|
|
|
69
69
|
notRegistered: not registered
|
|
70
70
|
remove: Remove
|
|
71
71
|
via: "via {origin}"
|
|
72
|
+
# LogStream (#4110)
|
|
73
|
+
log:
|
|
74
|
+
streaming: Streaming
|
|
75
|
+
done: Complete
|
|
76
|
+
stopped: Stopped
|
|
77
|
+
failed: "Failed: {error}"
|
|
78
|
+
lines: "{count} lines"
|
|
79
|
+
search: Search the log
|
|
80
|
+
previous: Previous match
|
|
81
|
+
next: Next match
|
|
82
|
+
onlyMatches: Only matching lines
|
|
83
|
+
errors: "{count} errors"
|
|
84
|
+
firstError: Go to the next error line
|
|
85
|
+
wrap: Wrap long lines
|
|
86
|
+
follow: Follow the end of the log
|
|
87
|
+
download: Download the log
|
|
88
|
+
newLines: "{count} new lines, resume"
|
|
89
|
+
empty: No output yet
|
|
90
|
+
# EntityTimeline (#4111)
|
|
91
|
+
timeline:
|
|
92
|
+
actors: Filter by actor
|
|
93
|
+
loading: Loading…
|
|
94
|
+
empty: Nothing happened yet
|
|
95
|
+
newEvents: "{count} new events"
|
|
96
|
+
oldestFirst: Oldest first
|
|
97
|
+
newestFirst: Newest first
|
|
98
|
+
# PayloadViewer / PayloadDiff (#4112)
|
|
99
|
+
payload:
|
|
100
|
+
headers: "Headers ({count})"
|
|
101
|
+
headerRow: First row is a header
|
|
102
|
+
copy: Copy
|
|
103
|
+
copied: Copied to the clipboard
|
|
104
|
+
copyFailed: Could not copy
|
|
105
|
+
download: Download
|
|
106
|
+
showAll: "Show all {count} rows"
|
|
107
|
+
binary: "Binary content, {size}. First bytes:"
|
|
108
|
+
malformed: "Not valid {format} ({error}), shown as text"
|
|
109
|
+
expected: Expected
|
|
110
|
+
captured: Captured
|
|
111
|
+
identical: Identical
|
|
112
|
+
unchanged: "{count} unchanged lines"
|
|
113
|
+
showSame: "Show {count} unchanged rows"
|
|
114
|
+
inline: Inline
|
|
115
|
+
split: Side by side
|
|
116
|
+
# EmailPreview (#4113)
|
|
117
|
+
email:
|
|
118
|
+
from: From
|
|
119
|
+
to: To
|
|
120
|
+
cc: Cc
|
|
121
|
+
replyTo: Reply to
|
|
122
|
+
date: Date
|
|
123
|
+
noSubject: (no subject)
|
|
124
|
+
allHeaders: "All headers ({count})"
|
|
125
|
+
noParser: "A raw message needs a parser: pass parseMime from @quazardous/qdadm/email. Showing the source."
|
|
126
|
+
tab:
|
|
127
|
+
html: HTML
|
|
128
|
+
text: Text
|
|
129
|
+
source: Source
|
|
130
|
+
imagesBlocked: "{count} remote images blocked"
|
|
131
|
+
loadImages: Load images
|
|
132
|
+
blockImages: Block images
|
|
133
|
+
empty: This email has no body
|
|
134
|
+
unnamed: (unnamed)
|
|
135
|
+
inline: inline
|
|
136
|
+
download: Download
|
|
137
|
+
# TaskRunner (#4114)
|
|
138
|
+
task:
|
|
139
|
+
status:
|
|
140
|
+
starting: Starting
|
|
141
|
+
queued: Queued
|
|
142
|
+
running: Running
|
|
143
|
+
succeeded: Done
|
|
144
|
+
failed: Failed
|
|
145
|
+
cancelled: Cancelled
|
|
146
|
+
cancel: Cancel the task
|
|
147
|
+
close: Close
|
|
148
|
+
background: Run in the background
|
|
149
|
+
open: Open the task
|
|
150
|
+
readError: "Could not read the task ({error}), still trying"
|
|
151
|
+
start: Start
|
|
152
|
+
dontStart: Do not start
|
|
153
|
+
typeToConfirm: "Type {value} to confirm"
|
|
154
|
+
tray: "{count} running tasks"
|
|
155
|
+
trayDone: Recent tasks
|
|
156
|
+
clear: Clear finished
|
|
72
157
|
# The breadcrumb's View↔Edit toggle (useNavContext).
|
|
73
158
|
breadcrumb:
|
|
74
159
|
view: View
|
|
@@ -71,6 +71,91 @@ export default `core:
|
|
|
71
71
|
notRegistered: non enregistrée
|
|
72
72
|
remove: Retirer
|
|
73
73
|
via: "via {origin}"
|
|
74
|
+
# LogStream (#4110)
|
|
75
|
+
log:
|
|
76
|
+
streaming: En cours
|
|
77
|
+
done: Terminé
|
|
78
|
+
stopped: Arrêté
|
|
79
|
+
failed: "Échec : {error}"
|
|
80
|
+
lines: "{count} lignes"
|
|
81
|
+
search: Chercher dans le log
|
|
82
|
+
previous: Occurrence précédente
|
|
83
|
+
next: Occurrence suivante
|
|
84
|
+
onlyMatches: Seulement les lignes trouvées
|
|
85
|
+
errors: "{count} erreurs"
|
|
86
|
+
firstError: Aller à la ligne d'erreur suivante
|
|
87
|
+
wrap: Couper les lignes longues
|
|
88
|
+
follow: Suivre la fin du log
|
|
89
|
+
download: Télécharger le log
|
|
90
|
+
newLines: "{count} nouvelles lignes, reprendre"
|
|
91
|
+
empty: Rien pour l'instant
|
|
92
|
+
# EntityTimeline (#4111)
|
|
93
|
+
timeline:
|
|
94
|
+
actors: Filtrer par acteur
|
|
95
|
+
loading: Chargement…
|
|
96
|
+
empty: Rien ne s'est encore passé
|
|
97
|
+
newEvents: "{count} nouveaux événements"
|
|
98
|
+
oldestFirst: Plus anciens d'abord
|
|
99
|
+
newestFirst: Plus récents d'abord
|
|
100
|
+
# PayloadViewer / PayloadDiff (#4112)
|
|
101
|
+
payload:
|
|
102
|
+
headers: "En-têtes ({count})"
|
|
103
|
+
headerRow: La première ligne est un en-tête
|
|
104
|
+
copy: Copier
|
|
105
|
+
copied: Copié dans le presse-papiers
|
|
106
|
+
copyFailed: Copie impossible
|
|
107
|
+
download: Télécharger
|
|
108
|
+
showAll: "Afficher les {count} lignes"
|
|
109
|
+
binary: "Contenu binaire, {size}. Premiers octets :"
|
|
110
|
+
malformed: "{format} invalide ({error}), affiché comme texte"
|
|
111
|
+
expected: Attendu
|
|
112
|
+
captured: Capturé
|
|
113
|
+
identical: Identiques
|
|
114
|
+
unchanged: "{count} lignes inchangées"
|
|
115
|
+
showSame: "Afficher les {count} lignes inchangées"
|
|
116
|
+
inline: En ligne
|
|
117
|
+
split: Côte à côte
|
|
118
|
+
# EmailPreview (#4113)
|
|
119
|
+
email:
|
|
120
|
+
from: De
|
|
121
|
+
to: À
|
|
122
|
+
cc: Cc
|
|
123
|
+
replyTo: Répondre à
|
|
124
|
+
date: Date
|
|
125
|
+
noSubject: (sans objet)
|
|
126
|
+
allHeaders: "Tous les en-têtes ({count})"
|
|
127
|
+
noParser: "Un message brut demande un parser : passer parseMime de @quazardous/qdadm/email. La source est affichée."
|
|
128
|
+
tab:
|
|
129
|
+
html: HTML
|
|
130
|
+
text: Texte
|
|
131
|
+
source: Source
|
|
132
|
+
imagesBlocked: "{count} images distantes bloquées"
|
|
133
|
+
loadImages: Charger les images
|
|
134
|
+
blockImages: Bloquer les images
|
|
135
|
+
empty: Ce mail n'a pas de contenu
|
|
136
|
+
unnamed: (sans nom)
|
|
137
|
+
inline: intégrée
|
|
138
|
+
download: Télécharger
|
|
139
|
+
# TaskRunner (#4114)
|
|
140
|
+
task:
|
|
141
|
+
status:
|
|
142
|
+
starting: Démarrage
|
|
143
|
+
queued: En attente
|
|
144
|
+
running: En cours
|
|
145
|
+
succeeded: Terminée
|
|
146
|
+
failed: Échouée
|
|
147
|
+
cancelled: Annulée
|
|
148
|
+
cancel: Annuler la tâche
|
|
149
|
+
close: Fermer
|
|
150
|
+
background: Continuer en arrière-plan
|
|
151
|
+
open: Ouvrir la tâche
|
|
152
|
+
readError: "Lecture de la tâche impossible ({error}), nouvel essai"
|
|
153
|
+
start: Lancer
|
|
154
|
+
dontStart: Ne pas lancer
|
|
155
|
+
typeToConfirm: "Taper {value} pour confirmer"
|
|
156
|
+
tray: "{count} tâches en cours"
|
|
157
|
+
trayDone: Tâches récentes
|
|
158
|
+
clear: Effacer les terminées
|
|
74
159
|
# The breadcrumb's View↔Edit toggle (useNavContext).
|
|
75
160
|
breadcrumb:
|
|
76
161
|
view: Voir
|