railcode 0.1.12 → 0.1.13

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 (3) hide show
  1. package/README.md +3 -3
  2. package/dist/index.js +21 -46
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -36,9 +36,9 @@ railcode db <list|query> ... List data connectors / run read-only SQL
36
36
  build command when needed, then uploads the output dir. The app is
37
37
  created-or-resolved by slug in your saved org, and the live URL is printed.
38
38
  - **`db`** inspects the org's **data connectors** (per-org Postgres) and runs
39
- ad-hoc read-only SQL. Run from an app dir for an app you've already deployed;
40
- it resolves the existing app by slug (it won't create one) and uses the same
41
- bearer, app-scoped routes the `dev` proxy forwards to.
39
+ ad-hoc read-only SQL. It works straight after `railcode login` **no app and no
40
+ `railcode.json` required** since connectors are org-scoped; it hits the app-less
41
+ `/api/organizations/{org}/data/*` plane with your login token.
42
42
  `railcode db list` (aliases `ls`, `connections`) prints each connector's
43
43
  `name` + `engine` (`--json` for the raw array). `railcode db query "<sql>"`
44
44
  (alias `sql`) runs SQL against `--connection` (default `default`) and prints a
package/dist/index.js CHANGED
@@ -353,17 +353,6 @@ async function resolveOrCreateApp(config, slug) {
353
353
  });
354
354
  return (await readJsonOrThrow(created, "Create app"));
355
355
  }
356
- // Resolve an existing app by slug WITHOUT creating it — for read-only commands
357
- // (e.g. `railcode db`) that must not spawn a phantom app as a side effect.
358
- async function resolveExistingApp(config, slug) {
359
- const orgPath = `/api/organizations/${config.orgUuid}/apps`;
360
- const list = (await readJsonOrThrow(await authedFetch(config, orgPath), "List apps"));
361
- const existing = list.find((app) => app.app_slug === slug);
362
- if (!existing) {
363
- throw new CliError(`App "${slug}" hasn't been created in your org yet. Run \`railcode deploy\` first.`, 1);
364
- }
365
- return existing;
366
- }
367
356
  async function postDeploy(config, appUuid, files) {
368
357
  // The deploy API is multipart: one part named "files" per file, with the
369
358
  // relative path carried as the part filename.
@@ -815,7 +804,7 @@ Query options:
815
804
  --json Print the raw { columns, rows, rowcount, truncated } envelope
816
805
 
817
806
  SQL is sent read-only with bound parameters — values are never interpolated into
818
- the query. Run from an app directory (a ${MANIFEST_NAME} with an "app" slug).
807
+ the query. Works right after \`railcode login\` — no app or ${MANIFEST_NAME} required.
819
808
  `;
820
809
  async function commandDb(args) {
821
810
  try {
@@ -851,8 +840,7 @@ async function commandDbList(args) {
851
840
  throw new CliError(`railcode db list takes no arguments (got "${args.rest[1]}").`, 2);
852
841
  }
853
842
  const config = requireLogin(args);
854
- const app = await resolveExistingApp(config, requireAppSlug());
855
- const connections = await fetchDbConnections(config, app.uuid);
843
+ const connections = await fetchDbConnections(config);
856
844
  if (args.options.json) {
857
845
  console.log(JSON.stringify(connections, null, 2));
858
846
  return;
@@ -874,10 +862,9 @@ async function commandDbQuery(args) {
874
862
  const connection = optionString(args, "connection") ?? "default";
875
863
  const query = source.kind === "file" ? readSqlFile(source.path) : source.sql;
876
864
  const config = requireLogin(args);
877
- const app = await resolveExistingApp(config, requireAppSlug());
878
865
  // --engine wins; otherwise look the engine up from the connector list.
879
- const engine = engineOption ?? inferEngine(await fetchDbConnections(config, app.uuid), connection);
880
- const result = await runDbQuery(config, app.uuid, { engine, connection, query, params });
866
+ const engine = engineOption ?? inferEngine(await fetchDbConnections(config), connection);
867
+ const result = await runDbQuery(config, { engine, connection, query, params });
881
868
  if (args.options.json) {
882
869
  console.log(JSON.stringify(result, null, 2));
883
870
  return;
@@ -889,22 +876,14 @@ async function commandDbQuery(args) {
889
876
  console.log("Note: results were truncated by the server.");
890
877
  }
891
878
  }
892
- function requireAppSlug() {
893
- const manifest = readManifest(process.cwd());
894
- if (!manifest?.app) {
895
- throw new CliError(`No ${MANIFEST_NAME} with an "app" slug here. Run \`railcode init <app>\` first, or add ${MANIFEST_NAME}.`, 2);
896
- }
897
- assertAppName(manifest.app);
898
- return manifest.app;
899
- }
900
- async function fetchDbConnections(config, appUuid) {
901
- const resp = await authedFetch(config, `/api/organizations/${config.orgUuid}/apps/${appUuid}/connections`);
879
+ async function fetchDbConnections(config) {
880
+ const resp = await authedFetch(config, `/api/organizations/${config.orgUuid}/data/connections`);
902
881
  if (resp.status === 401)
903
882
  await reLoginOrThrow();
904
883
  return (await readJsonOrThrow(resp, "List connections"));
905
884
  }
906
- async function runDbQuery(config, appUuid, body) {
907
- const resp = await authedFetch(config, `/api/organizations/${config.orgUuid}/apps/${appUuid}/sql`, {
885
+ async function runDbQuery(config, body) {
886
+ const resp = await authedFetch(config, `/api/organizations/${config.orgUuid}/data/sql`, {
908
887
  method: "POST",
909
888
  headers: { "Content-Type": "application/json" },
910
889
  body: JSON.stringify(body),
@@ -943,9 +922,8 @@ function camelToKebab(value) {
943
922
  // (`railcode connector list|fetch`)
944
923
  //
945
924
  // Service connectors are org-configured HTTP proxies to downstream SaaS APIs.
946
- // The app is the scoping/attribution context, so like `railcode db` — we
947
- // resolve (never create) the app by slug and use the bearer, app-scoped routes
948
- // `railcode dev` forwards to.
925
+ // Like `railcode db`, these one-shot commands hit the org-scoped `/data/*` plane
926
+ // with the login bearer token no app (and no railcode.json) required.
949
927
  // ---------------------------------------------------------------------------
950
928
  const CONNECTOR_HELP = `railcode connector — service connectors
951
929
 
@@ -972,8 +950,8 @@ Fetch options:
972
950
  --json Print the raw { status, ok, headers, body, truncated } envelope
973
951
 
974
952
  The connector holds the credential; you control only method/path/body. A non-2xx
975
- upstream status is still printed but exits non-zero. Run from an app directory (a
976
- ${MANIFEST_NAME} with an "app" slug).
953
+ upstream status is still printed but exits non-zero. Works right after
954
+ \`railcode login\` — no app or ${MANIFEST_NAME} required.
977
955
  `;
978
956
  async function commandConnector(args) {
979
957
  try {
@@ -1013,8 +991,7 @@ async function commandConnectorList(args) {
1013
991
  throw new CliError(`railcode connector list takes no arguments (got "${args.rest[1]}").`, 2);
1014
992
  }
1015
993
  const config = requireLogin(args);
1016
- const app = await resolveExistingApp(config, requireAppSlug());
1017
- const connectors = await fetchServiceConnectors(config, app.uuid);
994
+ const connectors = await fetchServiceConnectors(config);
1018
995
  if (args.options.json) {
1019
996
  console.log(JSON.stringify(connectors, null, 2));
1020
997
  return;
@@ -1040,8 +1017,7 @@ async function commandConnectorFetch(args) {
1040
1017
  : null;
1041
1018
  const payload = buildConnectorRequest({ connector, method, path: args.rest[1] ?? "", body });
1042
1019
  const config = requireLogin(args);
1043
- const app = await resolveExistingApp(config, requireAppSlug());
1044
- const envelope = await runConnectorRequest(config, app.uuid, payload);
1020
+ const envelope = await runConnectorRequest(config, payload);
1045
1021
  if (args.options.json) {
1046
1022
  console.log(JSON.stringify(envelope, null, 2));
1047
1023
  }
@@ -1065,8 +1041,7 @@ async function commandConnectorDocs(args) {
1065
1041
  throw new CliError("Pass either --json or --openapi, not both.", 2);
1066
1042
  }
1067
1043
  const config = requireLogin(args);
1068
- const app = await resolveExistingApp(config, requireAppSlug());
1069
- const docs = await fetchServiceConnectorDocs(config, app.uuid, name);
1044
+ const docs = await fetchServiceConnectorDocs(config, name);
1070
1045
  if (args.options.json) {
1071
1046
  console.log(JSON.stringify(docs, null, 2));
1072
1047
  return;
@@ -1081,14 +1056,14 @@ async function commandConnectorDocs(args) {
1081
1056
  }
1082
1057
  console.log(renderConnectorDocs(docs));
1083
1058
  }
1084
- async function fetchServiceConnectors(config, appUuid) {
1085
- const resp = await authedFetch(config, `/api/organizations/${config.orgUuid}/apps/${appUuid}/service-connectors`);
1059
+ async function fetchServiceConnectors(config) {
1060
+ const resp = await authedFetch(config, `/api/organizations/${config.orgUuid}/data/service-connectors`);
1086
1061
  if (resp.status === 401)
1087
1062
  await reLoginOrThrow();
1088
1063
  return (await readJsonOrThrow(resp, "List service connectors"));
1089
1064
  }
1090
- async function fetchServiceConnectorDocs(config, appUuid, name) {
1091
- const resp = await authedFetch(config, `/api/organizations/${config.orgUuid}/apps/${appUuid}/service-connectors/${encodeURIComponent(name)}`);
1065
+ async function fetchServiceConnectorDocs(config, name) {
1066
+ const resp = await authedFetch(config, `/api/organizations/${config.orgUuid}/data/service-connectors/${encodeURIComponent(name)}`);
1092
1067
  if (resp.status === 401)
1093
1068
  await reLoginOrThrow();
1094
1069
  if (resp.status === 404) {
@@ -1096,8 +1071,8 @@ async function fetchServiceConnectorDocs(config, appUuid, name) {
1096
1071
  }
1097
1072
  return (await readJsonOrThrow(resp, "Connector docs"));
1098
1073
  }
1099
- async function runConnectorRequest(config, appUuid, body) {
1100
- const resp = await authedFetch(config, `/api/organizations/${config.orgUuid}/apps/${appUuid}/service-connectors/request`, {
1074
+ async function runConnectorRequest(config, body) {
1075
+ const resp = await authedFetch(config, `/api/organizations/${config.orgUuid}/data/service-connectors/request`, {
1101
1076
  method: "POST",
1102
1077
  headers: { "Content-Type": "application/json" },
1103
1078
  body: JSON.stringify(body),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "railcode",
3
- "version": "0.1.12",
3
+ "version": "0.1.13",
4
4
  "description": "Developer CLI for the multi-tenant Railcode platform: log in, scaffold, and deploy static apps.",
5
5
  "license": "MIT",
6
6
  "type": "module",