@jtl-software/create-cloud-app 0.0.16 → 0.0.18

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
@@ -2,6 +2,10 @@
2
2
 
3
3
  Scaffold a [JTL Platform](https://www.jtl-software.com/) cloud app in seconds.
4
4
 
5
+ ## Requirements
6
+
7
+ Node.js **24** (current LTS) or newer. The scaffolded project sets the same floor via `engines.node`.
8
+
5
9
  ## Quick start
6
10
 
7
11
  ```bash
package/dist/index.js CHANGED
@@ -20,6 +20,7 @@ var REDIRECT_URI = `http://localhost:${CALLBACK_PORT}/callback`;
20
20
  var DEFAULT_SCOPE = "openid offline_access";
21
21
  var DEFAULT_API_HOST = "https://api.jtl-cloud.com";
22
22
  var DEFAULT_AUTH_HOST = "https://auth.jtl-cloud.com";
23
+ var DEFAULT_HUB_HOST = "https://hub.jtl-cloud.com";
23
24
  var CLIENT_IDS_BY_AUTH_HOST = {
24
25
  "auth.jtl-cloud.com": "b6aca875-c9a3-4010-9fd7-7f8d2e0b0333",
25
26
  "auth.dev.jtl-cloud.com": "9c230937-6a52-48d6-9a39-e97edd8ef511",
@@ -521,7 +522,8 @@ async function listTenantApps(apiBaseUrl, jwt, tenantId) {
521
522
  id,
522
523
  technicalName,
523
524
  clientId: entry.app?.clientId,
524
- provisioningStatus: entry.app?.activeVersion?.provisioningStatus
525
+ provisioningStatus: entry.app?.activeVersion?.provisioningStatus,
526
+ isInstalled: entry.isInstalled
525
527
  };
526
528
  }).filter((a) => a !== null);
527
529
  }
@@ -553,6 +555,7 @@ function resolveOptions(args) {
553
555
  options: {
554
556
  "api-host": { type: "string" },
555
557
  "auth-host": { type: "string" },
558
+ "hub-host": { type: "string" },
556
559
  "client-id": { type: "string" },
557
560
  scope: { type: "string" },
558
561
  reauth: { type: "boolean" },
@@ -570,6 +573,7 @@ function resolveOptions(args) {
570
573
  }
571
574
  const api = parsed.values["api-host"] ? validateHost(parsed.values["api-host"], "api-host") : DEFAULT_API_HOST;
572
575
  const auth = parsed.values["auth-host"] ? validateHost(parsed.values["auth-host"], "auth-host") : DEFAULT_AUTH_HOST;
576
+ const hub = parsed.values["hub-host"] ? validateHost(parsed.values["hub-host"], "hub-host") : DEFAULT_HUB_HOST;
573
577
  const clientIdArg = parsed.values["client-id"];
574
578
  const clientId = clientIdArg ?? CLIENT_IDS_BY_AUTH_HOST[new URL(auth).hostname] ?? "";
575
579
  if (!clientId) {
@@ -587,6 +591,7 @@ function resolveOptions(args) {
587
591
  return {
588
592
  api,
589
593
  auth,
594
+ hub,
590
595
  clientId,
591
596
  scope: parsed.values.scope ?? DEFAULT_SCOPE,
592
597
  reauth: parsed.values.reauth ?? false,
@@ -758,6 +763,25 @@ async function provisionApp(apiBaseUrl, jwt, tenantId, appId) {
758
763
  }
759
764
  return { clientId, clientSecret };
760
765
  }
766
+ function buildHubInstallUrl(hubHost, tenantSlug, appId) {
767
+ return `${hubHost}/${tenantSlug}/admin/app-overview/manage/${appId}/grants`;
768
+ }
769
+ function buildHubAppOverviewUrl(hubHost, tenantSlug) {
770
+ return `${hubHost}/${tenantSlug}/admin/app-overview?tab=development-apps`;
771
+ }
772
+ function printHubLink(hubHost, tenantSlug, appId, isInstalled) {
773
+ if (isInstalled) {
774
+ p.note(
775
+ pc.cyan(buildHubAppOverviewUrl(hubHost, tenantSlug)),
776
+ "Already installed \u2014 open in Hub"
777
+ );
778
+ } else {
779
+ p.note(
780
+ pc.cyan(buildHubInstallUrl(hubHost, tenantSlug, appId)),
781
+ "Install in Hub"
782
+ );
783
+ }
784
+ }
761
785
  function credentialLabels(backend) {
762
786
  if (backend.kind === "node") {
763
787
  return {
@@ -772,7 +796,7 @@ function credentialLabels(backend) {
772
796
  pair: "JtlPlatform:ClientId/ClientSecret"
773
797
  };
774
798
  }
775
- async function persistCredentials(backend, clientId, clientSecret, root, opts) {
799
+ async function persistCredentials(backend, clientId, clientSecret, root, opts, context) {
776
800
  const targetPath = backend.kind === "node" ? backend.envFile : backend.settingsFile;
777
801
  const relPath = path.relative(root, targetPath) || targetPath;
778
802
  const labels = credentialLabels(backend);
@@ -788,10 +812,22 @@ async function persistCredentials(backend, clientId, clientSecret, root, opts) {
788
812
  console.log(pc.cyan(labels.id) + " = " + clientId);
789
813
  console.log(pc.cyan(labels.secret) + " = " + clientSecret);
790
814
  console.log();
815
+ printHubLink(
816
+ context.hub,
817
+ context.tenantSlug,
818
+ context.appId,
819
+ context.isInstalled
820
+ );
791
821
  p.outro(pc.green("Done. Credentials printed above; file left untouched."));
792
822
  return;
793
823
  }
794
824
  const written = writeBackendCredentials(backend, clientId, clientSecret);
825
+ printHubLink(
826
+ context.hub,
827
+ context.tenantSlug,
828
+ context.appId,
829
+ context.isInstalled
830
+ );
795
831
  p.outro(
796
832
  pc.green(
797
833
  `Done. Wrote ${labels.pair} to ${pc.cyan(path.relative(root, written) || written)}.`
@@ -811,6 +847,7 @@ async function register(argv = [], runOpts = {}) {
811
847
  [
812
848
  `${pc.dim("API")} ${opts.api}`,
813
849
  `${pc.dim("Auth")} ${opts.auth}`,
850
+ `${pc.dim("Hub")} ${opts.hub}`,
814
851
  `${pc.dim("Client ID")} ${opts.clientId}`,
815
852
  `${pc.dim("Scope")} ${opts.scope}`
816
853
  ].join("\n"),
@@ -876,6 +913,12 @@ async function register(argv = [], runOpts = {}) {
876
913
  spinner3.stop("Failed to update manifest");
877
914
  bail(err.message);
878
915
  }
916
+ printHubLink(
917
+ opts.hub,
918
+ tenant.slug,
919
+ existing.id,
920
+ existing.isInstalled ?? false
921
+ );
879
922
  if (existing.clientId) {
880
923
  const labels = credentialLabels(backend);
881
924
  const envFile = backend.kind === "node" ? backend.envFile : backend.settingsFile;
@@ -923,7 +966,13 @@ async function register(argv = [], runOpts = {}) {
923
966
  spinner3.stop("Failed to provision app");
924
967
  bail(err.message);
925
968
  }
926
- await persistCredentials(backend, clientId, clientSecret, root, opts);
969
+ await persistCredentials(backend, clientId, clientSecret, root, opts, {
970
+ hub: opts.hub,
971
+ tenantSlug: tenant.slug,
972
+ appId,
973
+ // A freshly created app can't be installed yet.
974
+ isInstalled: false
975
+ });
927
976
  }
928
977
 
929
978
  // src/scaffold.ts
@@ -1072,6 +1121,9 @@ async function scaffold({
1072
1121
  name: appName,
1073
1122
  version: "1.0.0",
1074
1123
  private: true,
1124
+ engines: {
1125
+ node: ">=24"
1126
+ },
1075
1127
  workspaces: ["packages/*"],
1076
1128
  scripts: {
1077
1129
  dev: "turbo dev",
package/package.json CHANGED
@@ -1,11 +1,14 @@
1
1
  {
2
2
  "name": "@jtl-software/create-cloud-app",
3
- "version": "0.0.16",
3
+ "version": "0.0.18",
4
4
  "type": "module",
5
5
  "description": "CLI tool for scaffolding JTL Platform cloud apps",
6
6
  "bin": {
7
7
  "create-cloud-app": "./dist/index.js"
8
8
  },
9
+ "engines": {
10
+ "node": ">=24"
11
+ },
9
12
  "files": [
10
13
  "dist"
11
14
  ],
@@ -15,10 +18,10 @@
15
18
  },
16
19
  "dependencies": {
17
20
  "@clack/prompts": "^1.2.0",
18
- "@jtl-software/cloud-app-template-backend-dotnet": "0.0.9",
19
- "@jtl-software/cloud-app-template-backend-node": "0.0.11",
20
- "@jtl-software/cloud-app-template-frontend-react": "0.0.13",
21
- "@jtl-software/cloud-app-template-shared": "0.0.4",
21
+ "@jtl-software/cloud-app-template-backend-dotnet": "0.0.10",
22
+ "@jtl-software/cloud-app-template-backend-node": "0.0.12",
23
+ "@jtl-software/cloud-app-template-frontend-react": "0.0.15",
24
+ "@jtl-software/cloud-app-template-shared": "0.0.5",
22
25
  "picocolors": "^1.1.1"
23
26
  },
24
27
  "devDependencies": {