dropsh 0.7.0 → 0.7.2

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/README.md CHANGED
@@ -235,10 +235,14 @@ environment variable:
235
235
  | 2 | `--connection <id>` | names an **entry** |
236
236
  | 3 | `$DROPSH_CONFIG` | names a **file** |
237
237
  | 4 | `$DROPSH_CONNECTION` | names an **entry** |
238
- | 5 | `./dropsh.config.js` | the cwd default |
238
+ | 5 | existing default config | `./dropsh.config.js` by default |
239
+ | 6 | sole named connection | only when the default is absent |
240
+ | 7 | default config path | lets loading report the missing config |
239
241
 
240
242
  `--config` and `--connection` given together is not an error: `--config` names a
241
- file and wins.
243
+ file and wins. When no selector is set and the default config does not exist,
244
+ exactly one `.js` file in the connections directory is selected automatically.
245
+ Two or more named connections remain ambiguous and require a selector.
242
246
 
243
247
  An unknown id is a hard error — non-zero exit, naming the id, the directory
244
248
  searched and the available ids. Nothing is prompted and nothing is created, so
@@ -662,6 +666,35 @@ export default {
662
666
  `@dropsh/plugin-*` packages), so `import { DropSHPlugin } from "dropsh/plugin"`
663
667
  is typed with no ambient `declare module`.
664
668
 
669
+ The connection-resolution API is available to embedders as well:
670
+
671
+ ```ts
672
+ import {
673
+ resolveConfigSource,
674
+ resolveConnectionsDir,
675
+ } from "dropsh/plugin";
676
+
677
+ const connectionsDir = resolveConnectionsDir({
678
+ flag: "/home/me/.config/dropsh/connections",
679
+ });
680
+ const config = resolveConfigSource({
681
+ connection: "staging",
682
+ connectionsDir,
683
+ });
684
+ // config.path and config.source identify the selected config file and selector.
685
+ ```
686
+
687
+ Embedders whose default config is not `./dropsh.config.js` can pass its path as
688
+ `defaultConfig`. An existing default wins over implicit sole-connection
689
+ selection; when it is absent, a sole named connection is selected instead.
690
+
691
+ The same entry point also exports `CWD_CONFIG_FILE`, `listConnectionIds`, and
692
+ `listConnections`, plus the `ConfigSource`, `ConfigSourceKind`,
693
+ `ConnectionEntry`, `ResolveConfigSourceInput`, and `ResolveConnectionsDirInput`
694
+ types. These exports use the same selector precedence and connections-directory
695
+ semantics as the CLI; see [Named connections](#named-connections--one-config-file-per-site)
696
+ for the full documentation.
697
+
665
698
  ### Composing plugins
666
699
 
667
700
  `composePlugins(...children)` bundles several plugins into one flat list, so an
@@ -176,6 +176,14 @@ async function importConfig(filePath) {
176
176
  throw new ConfigError("site section missing");
177
177
  if (typeof site.base_url !== "string" || site.base_url.length === 0)
178
178
  throw new ConfigError("site.base_url required");
179
+ // Reject rather than silently trim, so the persisted config stays canonical.
180
+ // A trailing slash survives into every string-concatenated URL a consumer
181
+ // builds from base_url — notably `${base_url}/oauth/token` — where the doubled
182
+ // slash addresses a route that is not the token endpoint (DROPSH-13).
183
+ if (site.base_url.endsWith("/")) {
184
+ const corrected = site.base_url.replace(/\/+$/, "");
185
+ throw new ConfigError(`site.base_url must not end with a trailing slash — set it to ${corrected}`);
186
+ }
179
187
  return {
180
188
  configPath,
181
189
  raw,
@@ -1,7 +1,7 @@
1
1
  /** The name of the config file dropsh falls back to in the current directory. */
2
2
  export declare const CWD_CONFIG_FILE = "dropsh.config.js";
3
3
  /** Which selector produced the resolved config path. */
4
- export type ConfigSourceKind = "flag-config" | "flag-connection" | "env-config" | "env-connection" | "cwd";
4
+ export type ConfigSourceKind = "flag-config" | "flag-connection" | "env-config" | "env-connection" | "default-config" | "sole-connection" | "cwd";
5
5
  /** The chosen config file plus the provenance of that choice. */
6
6
  export interface ConfigSource {
7
7
  path: string;
@@ -25,6 +25,8 @@ export interface ResolveConfigSourceInput {
25
25
  config?: string | undefined;
26
26
  /** `--connection <id>`. */
27
27
  connection?: string | undefined;
28
+ /** Embedder-owned default config, used ahead of an implicit sole connection. */
29
+ defaultConfig?: string | undefined;
28
30
  connectionsDir?: string;
29
31
  env?: NodeJS.ProcessEnv;
30
32
  cwd?: string;
@@ -32,8 +34,10 @@ export interface ResolveConfigSourceInput {
32
34
  /**
33
35
  * Resolve the config file for this invocation, highest selector first:
34
36
  * `--config` > `--connection` > `$DROPSH_CONFIG` > `$DROPSH_CONNECTION` >
35
- * `./dropsh.config.js`. `--config` and `--connection` together is not an error:
36
- * `--config` names a *file* and that is its stated meaning, so it wins silently.
37
+ * an existing default config > the sole named connection > the default path.
38
+ * The default path is `./dropsh.config.js`. `--config` and `--connection`
39
+ * together is not an error: `--config` names a *file* and that is its stated
40
+ * meaning, so it wins silently.
37
41
  *
38
42
  * Returns the path *and* its provenance, so every caller — the command context,
39
43
  * the auth wiring, the pre-parse plugin bootstrap and `connections list` — reads
@@ -26,8 +26,8 @@ function toIds(entries) {
26
26
  }
27
27
  /**
28
28
  * Sorted ids of the `.js` files in `dir`; `[]` when the directory is absent.
29
- * Synchronous because it is only reached on the unknown-id error path, and the
30
- * resolver itself has to stay synchronous for the commander option wiring.
29
+ * Synchronous because the resolver uses it for implicit sole-connection
30
+ * selection and has to stay synchronous for the commander option wiring.
31
31
  */
32
32
  function listConnectionIdsSync(dir) {
33
33
  try {
@@ -40,8 +40,10 @@ function listConnectionIdsSync(dir) {
40
40
  /**
41
41
  * Resolve the config file for this invocation, highest selector first:
42
42
  * `--config` > `--connection` > `$DROPSH_CONFIG` > `$DROPSH_CONNECTION` >
43
- * `./dropsh.config.js`. `--config` and `--connection` together is not an error:
44
- * `--config` names a *file* and that is its stated meaning, so it wins silently.
43
+ * an existing default config > the sole named connection > the default path.
44
+ * The default path is `./dropsh.config.js`. `--config` and `--connection`
45
+ * together is not an error: `--config` names a *file* and that is its stated
46
+ * meaning, so it wins silently.
45
47
  *
46
48
  * Returns the path *and* its provenance, so every caller — the command context,
47
49
  * the auth wiring, the pre-parse plugin bootstrap and `connections list` — reads
@@ -66,7 +68,17 @@ export function resolveConfigSource(input = {}) {
66
68
  const id = env.DROPSH_CONNECTION;
67
69
  return { path: requireConnection(dir, id), source: "env-connection", id };
68
70
  }
69
- return { path: join(cwd, CWD_CONFIG_FILE), source: "cwd" };
71
+ const defaultConfig = input.defaultConfig ?? join(cwd, CWD_CONFIG_FILE);
72
+ const defaultSource = input.defaultConfig === undefined ? "cwd" : "default-config";
73
+ if (existsSync(defaultConfig))
74
+ return { path: defaultConfig, source: defaultSource };
75
+ const available = listConnectionIdsSync(dir);
76
+ if (available.length === 1) {
77
+ const id = available[0];
78
+ if (id)
79
+ return { path: connectionPath(dir, id), source: "sole-connection", id };
80
+ }
81
+ return { path: defaultConfig, source: defaultSource };
70
82
  }
71
83
  /**
72
84
  * The path of connection `id`, or a `ConfigError` naming the id, the directory
@@ -5,6 +5,8 @@ export type { AnyRenderer, InteractiveRenderer, RenderContext, Renderer, RenderS
5
5
  export { indexIncluded, isInteractive } from "./core/cli/render.js";
6
6
  export type { Config, SiteConfig } from "./core/config.js";
7
7
  export { loadConfig } from "./core/config.js";
8
+ export type { ConfigSource, ConfigSourceKind, ConnectionEntry, ResolveConfigSourceInput, ResolveConnectionsDirInput, } from "./core/connections.js";
9
+ export { CWD_CONFIG_FILE, listConnectionIds, listConnections, resolveConfigSource, resolveConnectionsDir, } from "./core/connections.js";
8
10
  export type { HttpClient, HttpRequest } from "./core/http.js";
9
11
  export { createHttpClient } from "./core/http.js";
10
12
  export type { DropSHOperation, JsonApiClient, JsonApiOptions, ResourceWriteBody, } from "./core/jsonapi/client.js";
@@ -1,6 +1,7 @@
1
1
  export { basicAuthPlugin, basicAuthProvider } from "./core/auth/basic.js";
2
2
  export { indexIncluded, isInteractive } from "./core/cli/render.js";
3
3
  export { loadConfig } from "./core/config.js";
4
+ export { CWD_CONFIG_FILE, listConnectionIds, listConnections, resolveConfigSource, resolveConnectionsDir, } from "./core/connections.js";
4
5
  export { createHttpClient } from "./core/http.js";
5
6
  export { createJsonApiClient } from "./core/jsonapi/client.js";
6
7
  export { buildPayloadFromParameters } from "./core/params/from-parameters.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dropsh",
3
- "version": "0.7.0",
3
+ "version": "0.7.2",
4
4
  "description": "Entity-agnostic helper CLI for Drupal 11 JSON:API, used by the dropsh Claude skill for editorial publishing workflows.",
5
5
  "type": "module",
6
6
  "bin": {