@loomcli/core 0.2.0 → 0.3.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.
Files changed (51) hide show
  1. package/dist/application.d.ts +59 -34
  2. package/dist/application.js +162 -59
  3. package/dist/chain.d.ts +25 -6
  4. package/dist/chain.js +99 -15
  5. package/dist/command.d.ts +146 -42
  6. package/dist/command.js +620 -78
  7. package/dist/environment.d.ts +22 -0
  8. package/dist/environment.js +1 -0
  9. package/dist/errors.d.ts +31 -59
  10. package/dist/errors.js +49 -106
  11. package/dist/extension.d.ts +5 -1
  12. package/dist/extension.js +18 -1
  13. package/dist/globals.d.ts +14 -25
  14. package/dist/globals.js +10 -61
  15. package/dist/glyphs.generated.d.ts +464 -0
  16. package/dist/glyphs.generated.js +491 -0
  17. package/dist/host.js +2 -1
  18. package/dist/index.d.ts +14 -5
  19. package/dist/index.js +5 -2
  20. package/dist/inspect.d.ts +26 -3
  21. package/dist/inspect.js +19 -5
  22. package/dist/lanes.d.ts +26 -0
  23. package/dist/lanes.js +45 -0
  24. package/dist/output.d.ts +93 -15
  25. package/dist/output.js +307 -34
  26. package/dist/plugin.d.ts +32 -16
  27. package/dist/plugin.js +46 -18
  28. package/dist/rendering.d.ts +21 -0
  29. package/dist/rendering.js +72 -0
  30. package/dist/sequence.d.ts +41 -0
  31. package/dist/sequence.js +225 -0
  32. package/dist/style-ansi.d.ts +13 -0
  33. package/dist/style-ansi.js +306 -0
  34. package/dist/style-layout.d.ts +29 -0
  35. package/dist/style-layout.js +228 -0
  36. package/dist/style-resolve.d.ts +6 -0
  37. package/dist/style-resolve.js +26 -0
  38. package/dist/style-state.d.ts +14 -0
  39. package/dist/style-state.js +179 -0
  40. package/dist/style-wire.d.ts +31 -0
  41. package/dist/style-wire.js +201 -0
  42. package/dist/style.d.ts +86 -0
  43. package/dist/style.js +201 -0
  44. package/dist/theme.d.ts +3 -0
  45. package/dist/theme.js +22 -0
  46. package/dist/types.d.ts +169 -21
  47. package/dist/validation.d.ts +8 -1
  48. package/dist/validation.js +17 -2
  49. package/dist/view.d.ts +180 -0
  50. package/dist/view.js +307 -0
  51. package/package.json +2 -1
package/dist/inspect.js CHANGED
@@ -17,11 +17,13 @@ function spellingsOf(table, name) {
17
17
  return spellings;
18
18
  }
19
19
  /**
20
- * A snapshot of one declared value. Arrays and plain objects are copied and frozen to any depth, so
21
- * a consumer cannot reach the declaration through the graph, and a later call reports the declared
22
- * value again. Primitives and library objects are reported as they are.
20
+ * A snapshot of one value. Arrays and plain objects are copied and frozen to any depth, so a
21
+ * consumer cannot reach the source through the copy, and a later call reports the value again.
22
+ * Primitives and library objects, such as a class instance or a `Date` a schema produced, are
23
+ * reported as they are, because core cannot copy them meaningfully. The graph reads it for a
24
+ * declared value and the chain reads it for the request one middleware holds.
23
25
  */
24
- function snapshot(value) {
26
+ export function snapshot(value) {
25
27
  if (Array.isArray(value)) {
26
28
  return Object.freeze(value.map((entry) => snapshot(entry)));
27
29
  }
@@ -111,9 +113,21 @@ function commandNode(command, place) {
111
113
  name: command.name,
112
114
  options: Object.freeze(optionNodes(command.inputs, { records, scope: 'application', table: command.options })),
113
115
  path,
116
+ result: resultNode(command.result),
114
117
  };
115
118
  return Object.freeze(node);
116
119
  }
120
+ /** The declared result as plain data, or `null` on a Command that declares none. */
121
+ function resultNode(result) {
122
+ if (!result) {
123
+ return null;
124
+ }
125
+ return Object.freeze({
126
+ default: result.default,
127
+ kind: result.kind,
128
+ views: Object.freeze([...result.views.keys()]),
129
+ });
130
+ }
117
131
  /**
118
132
  * Renders one built graph as frozen plain data. Nothing here reads a host fact or a schema. The
119
133
  * globals list holds the application's own options, then each installed plugin's in installation
@@ -138,4 +152,4 @@ function inspectGraph(name, graph, facts) {
138
152
  };
139
153
  return Object.freeze(inspected);
140
154
  }
141
- export { inspectGraph };
155
+ export { inspectGraph, resultNode };
@@ -0,0 +1,26 @@
1
+ import type { AnyDeclaredView, DeclaredView } from './view.js';
2
+ /** The five semantic methods, each with the glyph and the style token its lane carries. */
3
+ type Lane = 'error' | 'info' | 'print' | 'success' | 'warn';
4
+ /**
5
+ * The five lane views core declares, one per semantic method. An application replaces the function
6
+ * of any of them through `views`, and the replacement owns the gutter the default supplies.
7
+ */
8
+ declare const lanes: Readonly<Record<Lane, DeclaredView<string>>>;
9
+ /**
10
+ * What one sequence that stopped early reports: the Command it belongs to, the rows its source
11
+ * produced before the stop, and the rows core wrote. `head` is no row, so it is not counted.
12
+ */
13
+ interface IncompleteResult {
14
+ path: readonly string[];
15
+ yielded: number;
16
+ written: number;
17
+ }
18
+ /**
19
+ * The line a sequence writes on stderr when it stopped before its end, so an empty result and a
20
+ * truncated one never read alike. An override that returns the empty string silences it.
21
+ */
22
+ declare const incompleteResult: DeclaredView<IncompleteResult>;
23
+ /** Every view core declares, so one build register holds their identities from the start. */
24
+ declare const coreViews: readonly AnyDeclaredView[];
25
+ export type { IncompleteResult, Lane };
26
+ export { coreViews, incompleteResult, lanes };
package/dist/lanes.js ADDED
@@ -0,0 +1,45 @@
1
+ import { routedSubject } from './errors.js';
2
+ import { glyph } from './glyphs.generated.js';
3
+ import { view } from './view.js';
4
+ /**
5
+ * The identity prefix core's own views take, the package name by the plugin-identity convention.
6
+ * Core compiles from `src` alone, so the name is spelled here rather than read from the manifest.
7
+ */
8
+ const core = '@loomcli/core';
9
+ /**
10
+ * The default function of a marked lane: the matching glyph and one space before the first line,
11
+ * and continuation lines indented by the measured gutter without repeating the glyph. The
12
+ * semantic method appends the newline, so the view returns none.
13
+ */
14
+ function marked(mark) {
15
+ return (message, context) => {
16
+ const glyphText = glyph[mark];
17
+ const gutter = ' '.repeat(context.width(glyphText) + 1);
18
+ return `${context.style[mark](glyphText)} ${message.replace(/(?<newline>\r?\n)(?!$)/gu, `$<newline>${gutter}`)}`;
19
+ };
20
+ }
21
+ /** One lane view over the original message string, line breaks and authored styles included. */
22
+ function lane(name, render) {
23
+ return view(`${core}/lanes/${name}`, { render });
24
+ }
25
+ /**
26
+ * The five lane views core declares, one per semantic method. An application replaces the function
27
+ * of any of them through `views`, and the replacement owns the gutter the default supplies.
28
+ */
29
+ const lanes = {
30
+ error: lane('error', marked('error')),
31
+ info: lane('info', marked('info')),
32
+ print: lane('print', (message) => message),
33
+ success: lane('success', marked('success')),
34
+ warn: lane('warn', marked('warning')),
35
+ };
36
+ /**
37
+ * The line a sequence writes on stderr when it stopped before its end, so an empty result and a
38
+ * truncated one never read alike. An override that returns the empty string silences it.
39
+ */
40
+ const incompleteResult = view(`${core}/results/incomplete`, {
41
+ render: ({ path, written, yielded }) => `Output is incomplete: ${routedSubject(path)} stopped after ${yielded} rows, ${written} written.\n`,
42
+ });
43
+ /** Every view core declares, so one build register holds their identities from the start. */
44
+ const coreViews = [...Object.values(lanes), incompleteResult];
45
+ export { coreViews, incompleteResult, lanes };
package/dist/output.d.ts CHANGED
@@ -1,5 +1,9 @@
1
1
  import type { Writable } from 'node:stream';
2
- import type { Host, Out } from './types.js';
2
+ import type { Lane } from './lanes.js';
3
+ import type { RenderingPolicy } from './rendering.js';
4
+ import type { Palette } from './style-state.js';
5
+ import type { ActionChannel, Host, OpenResult, Out, ResultBinding, ViewContext } from './types.js';
6
+ import type { ViewRegistry } from './view.js';
3
7
  type WriteState = {
4
8
  kind: 'ok';
5
9
  } | {
@@ -7,44 +11,118 @@ type WriteState = {
7
11
  error: unknown;
8
12
  };
9
13
  /** The semantic calls, which choose a destination. A rendered value has no purpose of its own. */
10
- type Purpose = 'print' | 'info' | 'success' | 'warn' | 'error';
14
+ type Purpose = Lane;
15
+ /** The two streams every write site names. */
16
+ type Stream = 'stdout' | 'stderr';
11
17
  export declare class Output {
12
- readonly host: Pick<Host, 'stdout' | 'stderr'>;
18
+ readonly host: Host;
19
+ private readonly signal;
13
20
  private readonly destinations;
14
21
  private renderFault;
15
- readonly out: Out;
16
- constructor(host: Pick<Host, 'stdout' | 'stderr'>);
17
- /** A semantic message is one line on its destination; only `print` writes to stdout. */
18
- emit(kind: Purpose, message: string): Promise<void>;
22
+ private readonly stops;
23
+ private route;
24
+ /**
25
+ * The channel every caller writes through. It is typed with the result left open, because the
26
+ * declaration a call answers to is checked where the action was authored.
27
+ */
28
+ readonly out: Out<OpenResult>;
29
+ private palette;
30
+ private policy;
31
+ private registry;
32
+ style: import("./style.js").ContextualStyle;
33
+ constructor(host: Host, signal: AbortSignal);
34
+ /**
35
+ * The channel one action receives. On a Command that declares a result nothing the action writes
36
+ * but the result reaches stdout: `print` and `render` move to stderr, and they move the view
37
+ * context with the destination, so capability detection follows the stream the bytes reach. The
38
+ * destination is decided here, from the declaration, and never from the view a run selected.
39
+ */
40
+ channel(binding: ResultBinding): ActionChannel;
41
+ /**
42
+ * One `out.results` call on the action's channel. The declaration decides the unit, the selected
43
+ * view decides how it renders, and stdout carries the result under either one. The selected
44
+ * view is the one a middleware named before the action dispatched, or the declaration's default
45
+ * when none did. A call the declaration does not answer for is a fault of the lane and writes
46
+ * nothing.
47
+ */
48
+ private results;
49
+ /**
50
+ * One fault of the results lane: the call rejects, and the same failure is reported after this
51
+ * invocation's primary outcome, so a call the action never awaited still turns a would-be 0 into
52
+ * 1 and one the action let propagate is reported once.
53
+ */
54
+ private resultFault;
55
+ /** The registry one invocation resolves through, republished as each contributor is read. */
56
+ useViews(registry: ViewRegistry): void;
57
+ /** The routed path, published once routing resolved it, which an incomplete sequence names. */
58
+ useRoute(path: readonly string[]): void;
59
+ /** What this invocation's output raised beside its calls, in the order it was raised. */
60
+ get stopped(): readonly unknown[];
61
+ configure(policy: RenderingPolicy, palette: Palette): void;
62
+ context(destination: Stream): ViewContext;
63
+ /**
64
+ * A semantic message is one line on its destination; only `print` writes to stdout. The message
65
+ * is checked before the lane view runs, and this call appends the one newline after it, so a
66
+ * lane view returns none and an override that returns the empty string still writes one.
67
+ */
68
+ emit(kind: Purpose, message: string, destination: Stream): Promise<void>;
19
69
  /**
20
70
  * The failure report of one invocation. Like `render`, the text is queued on its destination
21
- * exactly as given: the caller already carries its own trailing newline, whether that text came
22
- * from a registered renderer or from core's own default text.
71
+ * after style resolution: the caller already carries its own trailing newline, whether that text
72
+ * came from a resolved view or from core's own default text.
23
73
  */
24
74
  report(text: string): Promise<void>;
25
75
  /**
26
- * The renderer owns every byte, so its text is queued on stdout exactly as returned. A throw or
27
- * a non-string return rejects this call alone: nothing is written for it, later output still
28
- * writes, and the recorded cause ends the invocation once the action has completed.
76
+ * One `out.render` call, dispatched on the shape of the view it was handed. The two shapes are
77
+ * exclusive, so a JavaScript author's value that carries both, or neither, is the output-view
78
+ * fault of this call and nothing is written for it.
79
+ */
80
+ private renderValue;
81
+ /**
82
+ * The view one sequence writes through, in the shape its own view carries. The caller supplies
83
+ * the contributors the view resolves through: `out.render`'s call-site view resolves through
84
+ * this invocation's registry, as ADR-0021 requires, and a result's view resolves through none,
85
+ * because a result's selected view is replaced by view name alone.
86
+ */
87
+ private sequenceView;
88
+ /**
89
+ * One sequence, which holds its place on the destination from here until its last piece is
90
+ * written. The returned rejection is observed here as well, because an action that never awaits
91
+ * the call must not end the process with an unhandled rejection.
92
+ */
93
+ private sequence;
94
+ /**
95
+ * The line one incomplete sequence writes on stderr, ahead of the fault's own report. It resolves
96
+ * through the registry like any other rendered output, so an override that returns the empty
97
+ * string silences it and one that throws is a view fault. A stderr that has failed already takes
98
+ * the plain fallback path and no further.
99
+ */
100
+ private incomplete;
101
+ /**
102
+ * The write site owns its newline; core resolves the view's marked text before queuing it. A
103
+ * throw or a non-string return rejects this call alone: nothing is written for it, later output
104
+ * still writes, and the recorded cause ends the invocation once the action has completed.
29
105
  */
30
106
  private rendered;
31
107
  /**
32
- * The rejected call. The first renderer failure is the reported one, so a later one adds no
108
+ * The rejected call. The first view failure is the reported one, so a later one adds no
33
109
  * second diagnostic, and the rejection is observed here as well, because an action that never
34
110
  * awaits the call must not end the process with an unhandled rejection.
35
111
  */
36
112
  private renderFailed;
37
- /** What a renderer failed with during this invocation, if one did. */
113
+ /** What a view failed with during this invocation, if one did. */
38
114
  get fault(): {
39
115
  cause: unknown;
40
116
  } | undefined;
117
+ /** The queue one stream writes through, opened the first time this invocation reaches it. */
118
+ private destination;
41
119
  private write;
42
120
  settle(): Promise<WriteState>;
43
121
  dispose(): void;
44
122
  }
45
123
  /**
46
124
  * The plain fallback path: a fresh destination on stderr, outside the invocation's queues and
47
- * outside every registration, so no application code runs on it. The caller composes the newlines
125
+ * outside every override, so no application code runs on it. The caller composes the newlines
48
126
  * between whatever it is reporting, then passes the one string this writes verbatim. A failed
49
127
  * write ends reporting.
50
128
  */