@hydranium/cli 1.0.0-next.277 → 1.0.0-next.279

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 (50) hide show
  1. package/README.md +5 -5
  2. package/lib/cli.js +3 -1
  3. package/lib/cli.js.map +1 -1
  4. package/lib/commands/client-session.d.ts +17 -0
  5. package/lib/commands/client-session.d.ts.map +1 -0
  6. package/lib/commands/client-session.js +27 -0
  7. package/lib/commands/client-session.js.map +1 -0
  8. package/lib/commands/create-args.d.ts +17 -0
  9. package/lib/commands/create-args.d.ts.map +1 -0
  10. package/lib/commands/create-args.js +53 -0
  11. package/lib/commands/create-args.js.map +1 -0
  12. package/lib/commands/create.d.ts +38 -0
  13. package/lib/commands/create.d.ts.map +1 -0
  14. package/lib/commands/create.js +47 -0
  15. package/lib/commands/create.js.map +1 -0
  16. package/lib/commands/delete.d.ts.map +1 -1
  17. package/lib/commands/delete.js +5 -14
  18. package/lib/commands/delete.js.map +1 -1
  19. package/lib/commands/harness-args.d.ts +7 -0
  20. package/lib/commands/harness-args.d.ts.map +1 -1
  21. package/lib/commands/harness-args.js +18 -0
  22. package/lib/commands/harness-args.js.map +1 -1
  23. package/lib/commands/init-templates.d.ts.map +1 -1
  24. package/lib/commands/init-templates.js +11 -12
  25. package/lib/commands/init-templates.js.map +1 -1
  26. package/lib/commands/save-args.d.ts.map +1 -1
  27. package/lib/commands/save-args.js +1 -0
  28. package/lib/commands/save-args.js.map +1 -1
  29. package/lib/commands/save.d.ts +4 -4
  30. package/lib/commands/save.d.ts.map +1 -1
  31. package/lib/commands/save.js +8 -44
  32. package/lib/commands/save.js.map +1 -1
  33. package/lib/index.d.ts +1 -0
  34. package/lib/index.d.ts.map +1 -1
  35. package/lib/index.js +1 -0
  36. package/lib/index.js.map +1 -1
  37. package/lib/spawn-data-server.d.ts +3 -3
  38. package/lib/spawn-data-server.js +3 -3
  39. package/package.json +10 -10
  40. package/src/cli.ts +3 -1
  41. package/src/commands/client-session.ts +33 -0
  42. package/src/commands/create-args.ts +68 -0
  43. package/src/commands/create.ts +86 -0
  44. package/src/commands/delete.ts +5 -13
  45. package/src/commands/harness-args.ts +19 -0
  46. package/src/commands/init-templates.ts +11 -12
  47. package/src/commands/save-args.ts +1 -0
  48. package/src/commands/save.ts +13 -53
  49. package/src/index.ts +1 -0
  50. package/src/spawn-data-server.ts +3 -3
@@ -33,6 +33,7 @@
33
33
 
34
34
  import { describeError, type LogThreshold } from '@hydranium/protocol';
35
35
  import { statSync } from 'node:fs';
36
+ import * as fs from 'node:fs/promises';
36
37
  import * as path from 'node:path';
37
38
  import { fileURLToPath } from 'node:url';
38
39
  import { type LogOptions, parseLogLevelOption } from '../log-level.js';
@@ -472,3 +473,21 @@ export function parseServerSpawnOptions(args: string[], commandName: string, onE
472
473
  }
473
474
  return { serverCommand, serverArgs, cwd, ...logOptions(logFlags), extra };
474
475
  }
476
+
477
+ /**
478
+ * Resolve the `--content` argument to its textual form. The `@<path>`
479
+ * prefix reads from a file (parses as UTF-8); anything else is taken
480
+ * literally. The leading `@` can be escaped as `\@` for content that
481
+ * legitimately starts with `@`.
482
+ */
483
+ export async function resolveContent(content: string, readFile?: (path: string) => Promise<string>): Promise<string> {
484
+ if (content.startsWith('@')) {
485
+ const file = content.slice(1);
486
+ const reader = readFile ?? ((target: string) => fs.readFile(target, 'utf8'));
487
+ return reader(file);
488
+ }
489
+ if (content.startsWith('\\@')) {
490
+ return content.slice(1);
491
+ }
492
+ return content;
493
+ }
@@ -1130,9 +1130,9 @@ void glspServer;
1130
1130
  data
1131
1131
  ? `
1132
1132
  //
1133
- // NOT the entry \`hydranium-cli query\` / \`save\` / \`projects\` / \`watch\` speak to:
1134
- // stdio here carries LSP, and the data head is a socket whose port is published
1135
- // over the LSP connection. Those subcommands spawn \`data-server-main.js\`.`
1133
+ // NOT the entry the data-server subcommands of \`hydranium-cli\` speak to: stdio
1134
+ // here carries LSP, and the data head is a socket whose port is published over
1135
+ // the LSP connection. Those subcommands spawn \`data-server-main.js\`.`
1136
1136
  : ''
1137
1137
  }
1138
1138
  //
@@ -1163,11 +1163,11 @@ ${dataBlock}${glspBlock}`;
1163
1163
  * Emitted with the `data` head because it is the ONLY command line that reaches
1164
1164
  * that head from outside the editor. `main.ts` gives stdio to LSP and publishes
1165
1165
  * the data head's socket port over the LSP connection, so a client that is not
1166
- * an LSP client cannot discover it — which is every one of `hydranium-cli`'s
1167
- * `query` / `save` / `projects` / `watch`, all of which drive the data protocol
1168
- * over a spawned child's stdin/stdout. Without this file a scaffolded project
1169
- * has no `--server` value, and the closest guess — the LSP entry — fails as an
1170
- * unrecognised method rather than as a missing head.
1166
+ * an LSP client cannot discover it — which is every data-server subcommand of
1167
+ * `hydranium-cli`, since each drives the data protocol over a spawned child's
1168
+ * stdin/stdout. Without this file a scaffolded project has no `--server` value,
1169
+ * and the closest guess — the LSP entry — fails as an unrecognised method
1170
+ * rather than as a missing head.
1171
1171
  *
1172
1172
  * A separate process rather than a flag on `main.ts`: only one protocol can own
1173
1173
  * stdio, so the two entries are mutually exclusive by construction.
@@ -1178,10 +1178,9 @@ function dataServerMainFile(composition: InitComposition): string {
1178
1178
  const plural = roots.length > 1 ? 's' : '';
1179
1179
 
1180
1180
  const text = `${SHEBANG}// Standalone entry point: a data-server head on **stdio**, with no LSP head in
1181
- // the process. This is the transport \`hydranium-cli\` speaks — \`query\`, \`save\`,
1182
- // \`projects\` and \`watch\` all spawn a server command and drive JSON-RPC over its
1183
- // stdin/stdout — so it is what makes those subcommands usable against this
1184
- // language.
1181
+ // the process. This is the transport \`hydranium-cli\` speaks — its data-server
1182
+ // subcommands spawn a server command and drive JSON-RPC over its stdin/stdout —
1183
+ // so it is what makes those subcommands usable against this language.
1185
1184
  //
1186
1185
  // Invocation: \`node lib/data-server-main.js [<workspace-path>]\`, or the
1187
1186
  // package's \`__PROJECT_ID__-data-server\` bin script. The workspace path defaults to
@@ -40,6 +40,7 @@ export const SAVE_HELP: readonly string[] = [
40
40
  'Usage: hydranium-cli save --server "<cmd>..." --uri <uri> --content <text|@file> [--client-id <id>] [--cwd <dir>] [--log-level <lvl>] [--log-file <file>] [--log-file-level <lvl>]',
41
41
  '',
42
42
  'Update the document at `<uri>` with `<content>` and persist via saveModelDocument.',
43
+ 'Fails when the file does not exist; `create` writes a new one.',
43
44
  'Output is the post-save envelope as a single JSON line.',
44
45
  '',
45
46
  'Options:',
@@ -7,11 +7,12 @@
7
7
  * SPDX-License-Identifier: MIT
8
8
  ********************************************************************************/
9
9
 
10
- import { describeError, type TransferElement } from '@hydranium/protocol';
10
+ import type { TransferElement } from '@hydranium/protocol';
11
11
  import type { DataServerProtocol } from '@hydranium/protocol/data';
12
- import * as fs from 'node:fs/promises';
13
12
  import { logEnv, type LogOptions } from '../log-level.js';
14
13
  import { withDataServer } from '../spawn-data-server.js';
14
+ import { withClientSession } from './client-session.js';
15
+ import { resolveContent } from './harness-args.js';
15
16
 
16
17
  /**
17
18
  * Options for the {@link runSave} subcommand. Updates the document at
@@ -25,9 +26,9 @@ import { withDataServer } from '../spawn-data-server.js';
25
26
  * identity (per-user, per-script) override.
26
27
  *
27
28
  * The command saves as a client session under `clientId`: it registers the
28
- * session, creates the document from the content when there is no file,
29
- * opens it otherwise, saves, and ends the session, which leaves nothing open
30
- * behind it.
29
+ * session, opens the document, saves, and ends the session, which leaves
30
+ * nothing open behind it. A missing file fails the open; `runCreate` writes a
31
+ * new one.
31
32
  */
32
33
  export interface SaveCommandOptions extends LogOptions {
33
34
  readonly serverCommand: string;
@@ -66,56 +67,15 @@ export async function runSave(options: SaveCommandOptions): Promise<void> {
66
67
  );
67
68
  }
68
69
 
69
- /**
70
- * Save `model` to `uri` as the session `clientId`, and end the session whether
71
- * the save succeeded or not. An end that fails after a failed save is
72
- * swallowed: the save's error is the one the user needs.
73
- */
74
- async function saveOpened(
75
- server: Pick<
76
- DataServerProtocol<TransferElement>,
77
- 'createSession' | 'openModelDocument' | 'createModelDocument' | 'saveModelDocument' | 'closeSession'
78
- >,
70
+ /** Open `uri` as the session `clientId` and save `model` to it, then end the session. */
71
+ function saveOpened(
72
+ server: Pick<DataServerProtocol<TransferElement>, 'createSession' | 'openModelDocument' | 'saveModelDocument' | 'closeSession'>,
79
73
  uri: string,
80
74
  clientId: string,
81
75
  model: string
82
76
  ): Promise<unknown> {
83
- await server.createSession({ clientId, label: 'hydranium-cli' });
84
- let saved: unknown;
85
- try {
86
- // The server creates a document only when it finds no file and no
87
- // client has it open; anything else is opened. An open that fails
88
- // reports why the create failed too, since that may be the real cause.
89
- await server.createModelDocument({ uri, clientId, text: model }).catch((created: unknown) =>
90
- server.openModelDocument({ uri, clientId }).catch((opened: unknown) => {
91
- throw new Error(`Cannot save ${uri}: ${describeError(opened)} (creating it failed first: ${describeError(created)})`, {
92
- cause: created
93
- });
94
- })
95
- );
96
- saved = await server.saveModelDocument({ uri, clientId, model, baseVersion: 'any' });
97
- } catch (error: unknown) {
98
- await server.closeSession({ clientId }).catch(() => undefined);
99
- throw error;
100
- }
101
- await server.closeSession({ clientId });
102
- return saved;
103
- }
104
-
105
- /**
106
- * Resolve the `--content` argument to its textual form. The `@<path>`
107
- * prefix reads from a file (parses as UTF-8); anything else is taken
108
- * literally. The leading `@` can be escaped as `\@` for content that
109
- * legitimately starts with `@`.
110
- */
111
- async function resolveContent(content: string, readFile?: (path: string) => Promise<string>): Promise<string> {
112
- if (content.startsWith('@')) {
113
- const path = content.slice(1);
114
- const reader = readFile ?? ((target: string) => fs.readFile(target, 'utf8'));
115
- return reader(path);
116
- }
117
- if (content.startsWith('\\@')) {
118
- return content.slice(1);
119
- }
120
- return content;
77
+ return withClientSession(server, clientId, async () => {
78
+ await server.openModelDocument({ uri, clientId });
79
+ return server.saveModelDocument({ uri, clientId, model, baseVersion: 'any' });
80
+ });
121
81
  }
package/src/index.ts CHANGED
@@ -7,6 +7,7 @@
7
7
  * SPDX-License-Identifier: MIT
8
8
  ********************************************************************************/
9
9
 
10
+ export * from './commands/create.js';
10
11
  export * from './commands/delete.js';
11
12
  export * from './commands/generate-ast-builder.js';
12
13
  export * from './commands/generate-transfer-model.js';
@@ -236,9 +236,9 @@ export function spawnDataServer<TTransfer extends TransferElement, TDiagnostic e
236
236
  * child down afterwards. The `use` call is raced against
237
237
  * {@link DataServerHandle.whenTerminated}, so a spawn failure or a premature child
238
238
  * exit rejects with a clear message instead of hanging on an RPC that can never be
239
- * answered. This is the one-shot counterpart the `projects` / `query` / `save`
240
- * subcommands compose; `watch` races its own long-running loop against
241
- * `whenTerminated` directly.
239
+ * answered. This is the one-shot counterpart the data-server subcommands
240
+ * compose; `watch` races its own long-running loop against `whenTerminated`
241
+ * directly.
242
242
  */
243
243
  export async function withDataServer<TTransfer extends TransferElement, TDiagnostic extends TransferDiagnostic, R>(
244
244
  options: SpawnDataServerOptions,