@quatronic/qore 0.1.10 → 0.1.11-test.266fd68

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
@@ -24,6 +24,7 @@ qore version # CLI + portal version info
24
24
  qore env pull # write this module's local secrets into an env file
25
25
  qore run -- <command> # run a command with those secrets in its environment
26
26
  qore roles list # roles in your code, on local, and yours there
27
+ qore roles sync # announce your code's roles for local, keep yours
27
28
  qore roles use <role…> # announce your code's roles for local and take these
28
29
 
29
30
  qore errors --env stag # recent unresolved errors
@@ -34,8 +35,8 @@ qore logs --env stag --level error # error logs
34
35
 
35
36
  ### Portals
36
37
 
37
- `qore` talks to `prod` by default. Pass `--portal staging` to use the
38
- staging portal, or `--portal https://your-portal.example` for anything
38
+ `qore` talks to `prod` by default. Pass `--portal staging` or
39
+ `--portal testing` to use those portals, or `--portal https://your-portal.example` for anything
39
40
  else (only accepted on `qore login`, which then pins the identity provider
40
41
  it reports).
41
42
 
@@ -149,7 +150,9 @@ the portal's own audit trail, which records key names only (see
149
150
 
150
151
  #### `qore roles`
151
152
 
152
- Run in the app's frontend module. `qore roles use inspector` reads the roles in
153
+ Run in the app's frontend module. `qore roles sync` does the announcing alone:
154
+ local learns every role in `permissions.ts` and your own roles stay as they are.
155
+ `qore roles use inspector` reads the roles in
153
156
  `src/lib/permissions.ts`, tells the portal they exist on `local` (only there —
154
157
  staging and production learn roles from the code that environment deploys:
155
158
  `main` for staging, the `production` branch for production), and makes exactly
package/dist/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="2eacac78-c916-5f6b-825a-d5af2c955f32")}catch(e){}}();
3
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="4be6a705-edee-51df-8b0e-e26c3410dcc4")}catch(e){}}();
4
4
  import { createRequire as __qoreCreateRequire } from "node:module"; const require = __qoreCreateRequire(import.meta.url);
5
5
  import {
6
6
  addSecretValues,
@@ -450,7 +450,7 @@ async function withProxyHint(deps2, e) {
450
450
  return proxy ? withSystemProxy(e, proxy, deps2.platform) : e;
451
451
  }
452
452
  function buildProgram(deps2, io2, signal, register, setExitCode) {
453
- const program = new Command("qore").description("Qore developer CLI").option("--portal <name|url>", "prod, staging, or a portal URL (default: the portal you last logged in to, else prod)").option("--json", "print exactly one JSON document on stdout").option("--no-input", "never prompt").option("--verbose", "log request lines (never secrets)").option("--no-color", "disable colours").exitOverride().configureOutput({ writeOut: (s) => io2.out(s.trimEnd()), writeErr: (s) => io2.err(s.trimEnd()) });
453
+ const program = new Command("qore").description("Qore developer CLI").option("--portal <name|url>", "prod, staging, testing, or a portal URL (default: the portal you last logged in to, else prod)").option("--json", "print exactly one JSON document on stdout").option("--no-input", "never prompt").option("--verbose", "log request lines (never secrets)").option("--no-color", "disable colours").exitOverride().configureOutput({ writeOut: (s) => io2.out(s.trimEnd()), writeErr: (s) => io2.err(s.trimEnd()) });
454
454
  const ctx = { deps: deps2, io: io2, signal, opts: () => program.opts(), setExitCode };
455
455
  for (const r of register) r(program, ctx);
456
456
  return program;
@@ -609,6 +609,10 @@ var BUILTIN_PORTALS = {
609
609
  staging: {
610
610
  apiOrigin: "https://p01--portal-api-staging--hcywmtb2vksv.code.run",
611
611
  pinnedIssuer: "https://auth-staging.quatronic.app/realms/platform-staging"
612
+ },
613
+ testing: {
614
+ apiOrigin: "https://api.qore-testing.quatronic.app",
615
+ pinnedIssuer: "https://auth-testing.quatronic.app/realms/platform-testing"
612
616
  }
613
617
  };
614
618
  function builtinFor(origin) {
@@ -623,7 +627,7 @@ function toOrigin(raw) {
623
627
  url = new URL(raw);
624
628
  } catch {
625
629
  throw new QoreError("QORE_CONTEXT_INVALID", `Not a portal name or URL: ${raw}`, {
626
- hint: "Use --portal prod, --portal staging or --portal https://\u2026"
630
+ hint: "Use --portal prod, --portal staging, --portal testing or --portal https://\u2026"
627
631
  });
628
632
  }
629
633
  const loopback = url.hostname === "localhost" || url.hostname === "127.0.0.1";
@@ -636,6 +640,7 @@ function resolvePortal(arg, defaultProfile) {
636
640
  const chosen = arg ?? defaultProfile;
637
641
  if (chosen === void 0 || chosen === "prod") return { ...BUILTIN_PORTALS.prod, builtin: "prod" };
638
642
  if (chosen === "staging") return { ...BUILTIN_PORTALS.staging, builtin: "staging" };
643
+ if (chosen === "testing") return { ...BUILTIN_PORTALS.testing, builtin: "testing" };
639
644
  const origin = toOrigin(chosen);
640
645
  return builtinFor(origin) ?? { apiOrigin: origin, pinnedIssuer: null, builtin: null };
641
646
  }
@@ -1990,7 +1995,7 @@ async function workspaceRoot(deps2) {
1990
1995
  }
1991
1996
  function profileOrder(cfg, cachedOrigins, portalArg, wsPortal) {
1992
1997
  if (portalArg) return [resolvePortal(portalArg, cfg.defaultProfile)];
1993
- const rank = (o) => o === BUILTIN_PORTALS.prod.apiOrigin ? 0 : o === BUILTIN_PORTALS.staging.apiOrigin ? 1 : 2;
1998
+ const rank = (o) => o === BUILTIN_PORTALS.prod.apiOrigin ? 0 : o === BUILTIN_PORTALS.staging.apiOrigin ? 1 : o === BUILTIN_PORTALS.testing.apiOrigin ? 2 : 3;
1994
1999
  const rest = Object.keys(cfg.profiles).sort((a, b) => rank(a) - rank(b) || a.localeCompare(b));
1995
2000
  const cached2 = cachedOrigins.filter((c) => cfg.profiles[c]);
1996
2001
  const origins = [...wsPortal ? [wsPortal.apiOrigin] : [], ...cached2, resolvePortal(void 0, cfg.defaultProfile).apiOrigin, ...rest];
@@ -3164,12 +3169,25 @@ async function wholeAppWanted(ctx) {
3164
3169
  const ws = await findWorkspace(ctx.deps.cwd, ctx.deps.home);
3165
3170
  const repo = ws && await findRepoRoot(ctx.deps.cwd);
3166
3171
  if (!ws || !repo || repo === ws.root || !isInside(ws.root, repo)) return null;
3172
+ if (await appModuleCount(ctx, ws) === 1) return null;
3167
3173
  const pick2 = await ctx.io.select("Which data model?", [
3168
3174
  { name: "the whole app", value: "app" },
3169
3175
  { name: `only ${basename3(repo)}`, value: "module" }
3170
3176
  ]);
3171
3177
  return pick2 === "app" ? ws : null;
3172
3178
  }
3179
+ async function appModuleCount(ctx, ws) {
3180
+ const g = ctx.opts();
3181
+ try {
3182
+ const portal = await flagPortal(ctx.deps, g.portal, ws);
3183
+ const session = await openSession(ctx.deps, portal, "db erd", g.verbose ? (l) => ctx.io.err(l) : void 0);
3184
+ await ensureCapability(ctx.deps, portal.apiOrigin, "app.modules");
3185
+ return (await workspaceAppModules(session, portal, ws)).modules.length;
3186
+ } catch (e) {
3187
+ if (e instanceof CancelledError) throw e;
3188
+ return null;
3189
+ }
3190
+ }
3173
3191
  async function serve(ctx, f, server) {
3174
3192
  try {
3175
3193
  const url = `http://127.0.0.1:${server.port}`;
@@ -5541,7 +5559,7 @@ async function checkRepoNode(deps2, m, recipe, pkg, signal, ms2) {
5541
5559
  return ok(`Node ${found ?? "?"} (engines ${pkg.engines.node})`);
5542
5560
  }
5543
5561
  async function loggedInPortals(deps2, profiles) {
5544
- const rank = (o) => o === BUILTIN_PORTALS.prod.apiOrigin ? 0 : o === BUILTIN_PORTALS.staging.apiOrigin ? 1 : 2;
5562
+ const rank = (o) => o === BUILTIN_PORTALS.prod.apiOrigin ? 0 : o === BUILTIN_PORTALS.staging.apiOrigin ? 1 : o === BUILTIN_PORTALS.testing.apiOrigin ? 2 : 3;
5545
5563
  const out = [];
5546
5564
  for (const origin of Object.keys(profiles).sort((a, b) => rank(a) - rank(b) || a.localeCompare(b))) {
5547
5565
  const p = profiles[origin];
@@ -6716,6 +6734,11 @@ async function useLocalRoles(deps2, mc, app, roles, use) {
6716
6734
  await ensureCapability(deps2, mc.portal.apiOrigin, CAPABILITY);
6717
6735
  return explained(mc, () => mc.session.api.post(path(mc, app), { roles, use }));
6718
6736
  }
6737
+ var ANNOUNCE_CAPABILITY = "module.local-roles-announce";
6738
+ async function announceLocalRoles(deps2, mc, app, roles) {
6739
+ await ensureCapability(deps2, mc.portal.apiOrigin, ANNOUNCE_CAPABILITY);
6740
+ return explained(mc, () => mc.session.api.post(path(mc, app), { roles }));
6741
+ }
6719
6742
  async function explained(mc, call) {
6720
6743
  try {
6721
6744
  return await call();
@@ -6846,9 +6869,27 @@ function registerRoles(program, ctx) {
6846
6869
  c.io.out(`Known on local ${[...known].join(", ") || "\u2014"}`);
6847
6870
  c.io.out(`Yours on local ${local.effective.join(", ") || "\u2014"}`);
6848
6871
  if (fresh.length) {
6849
- c.io.out(`New in your code: ${fresh.join(", ")} \u2014 \`qore roles use <role...>\` announces them and makes them yours on local.`);
6872
+ c.io.out(`New in your code: ${fresh.join(", ")} \u2014 \`qore roles sync\` makes local know them; \`qore roles use <role...>\` also makes them yours.`);
6850
6873
  }
6851
6874
  });
6875
+ withFlags(roles.command("sync").description("Announce your code's roles for local; your own roles stay as they are")).action(async (f) => {
6876
+ const { c, mc, app, code } = await rolesContext(ctx, f, "roles sync");
6877
+ const res = await announceLocalRoles(c.deps, mc, app, code);
6878
+ if (c.opts().json) {
6879
+ renderJson(c.io, {
6880
+ context: contextJson(mc, app),
6881
+ added: res.added,
6882
+ local: res.roles.map((r) => r.slug),
6883
+ direct: res.direct,
6884
+ effective: res.effective
6885
+ });
6886
+ return;
6887
+ }
6888
+ c.io.err(contextLine(mc, app));
6889
+ c.io.out(`\u2713 Known on local: ${res.roles.map((r) => r.slug).join(", ")}`);
6890
+ if (res.added.length) c.io.out(` New on local: ${res.added.join(", ")} \u2014 teammates can pick it in the Access tab too.`);
6891
+ c.io.out(` Your roles on local: ${res.effective.join(", ") || "\u2014"} (unchanged; \`qore roles use <role...>\` to change them)`);
6892
+ });
6852
6893
  withFlags(roles.command("use").description("Announce your code's roles for local and make exactly these yours there").argument("<role...>", "the roles you want on local")).action(async (wanted, f) => {
6853
6894
  const { c, mc, app, code } = await rolesContext(ctx, f, "roles use");
6854
6895
  const inCode = new Set(code.map((r) => r.slug));
@@ -7921,7 +7962,12 @@ async function connectPostgres(url, o) {
7921
7962
 
7922
7963
  // src/buildInfo.ts
7923
7964
  var SENTRY_DSN = true ? "https://50b592efb53dbc1a31cb10d3945c6d52@o4512129906049024.ingest.de.sentry.io/4512174341750864" : "";
7924
- var SENTRY_ENVIRONMENT = true ? "production" : "development";
7965
+ function sentryEnvironment(version) {
7966
+ if (version === "0.0.0-dev") return "development";
7967
+ if (version.includes("-rc.")) return "next";
7968
+ if (version.includes("-test.")) return "test";
7969
+ return "production";
7970
+ }
7925
7971
 
7926
7972
  // src/platform/proxy.ts
7927
7973
  import { execFile as execFile3 } from "child_process";
@@ -7985,7 +8031,16 @@ function systemProxy(platform = process.platform) {
7985
8031
  }
7986
8032
 
7987
8033
  // src/version.ts
7988
- var VERSION = true ? "0.1.10" : "0.0.0-dev";
8034
+ import { readFileSync } from "fs";
8035
+ function readVersion() {
8036
+ try {
8037
+ const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
8038
+ return typeof pkg.version === "string" ? pkg.version : "0.0.0-dev";
8039
+ } catch {
8040
+ return "0.0.0-dev";
8041
+ }
8042
+ }
8043
+ var VERSION = readVersion();
7989
8044
 
7990
8045
  // src/cli.ts
7991
8046
  if (process.env.HTTPS_PROXY || process.env.https_proxy || process.env.HTTP_PROXY) setGlobalDispatcher(new EnvHttpProxyAgent());
@@ -8032,7 +8087,7 @@ var deps = {
8032
8087
  telemetry.registerSecrets(values);
8033
8088
  },
8034
8089
  sentryDsn: SENTRY_DSN,
8035
- sentryEnvironment: SENTRY_ENVIRONMENT,
8090
+ sentryEnvironment: sentryEnvironment(VERSION),
8036
8091
  systemProxy: () => systemProxy(process.platform),
8037
8092
  viewerDir: fileURLToPath(new URL("../viewer/dist/", import.meta.url)),
8038
8093
  postgres: connectPostgres
@@ -8065,4 +8120,4 @@ if (!drained) {
8065
8120
  process.exit();
8066
8121
  }
8067
8122
  //# sourceMappingURL=cli.js.map
8068
- //# debugId=2eacac78-c916-5f6b-825a-d5af2c955f32
8123
+ //# debugId=4be6a705-edee-51df-8b0e-e26c3410dcc4
@@ -0,0 +1,11 @@
1
+ {
2
+ "capabilities": [
3
+ "app.modules",
4
+ "module.local-env",
5
+ "module.local-roles",
6
+ "module.local-roles-announce",
7
+ "module.observability",
8
+ "module.status",
9
+ "repo.resolve"
10
+ ]
11
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quatronic/qore",
3
- "version": "0.1.10",
3
+ "version": "0.1.11-test.266fd68",
4
4
  "description": "Qore developer CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -8,6 +8,7 @@
8
8
  },
9
9
  "files": [
10
10
  "dist/**/*.js",
11
+ "dist/qore-capabilities.json",
11
12
  "schema",
12
13
  "viewer/dist",
13
14
  "README.md"
@@ -25,7 +26,7 @@
25
26
  "url": "git+https://github.com/Quatronic-IT/qore-cli.git"
26
27
  },
27
28
  "scripts": {
28
- "build": "tsup && vite build --config viewer/vite.config.ts",
29
+ "build": "tsup && vite build --config viewer/vite.config.ts && node scripts/capabilities.mjs > dist/qore-capabilities.json",
29
30
  "build:viewer": "vite build --config viewer/vite.config.ts",
30
31
  "test": "vitest run",
31
32
  "test:watch": "vitest",