@apso/cli 0.44.0 → 0.45.0

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 (35) hide show
  1. package/dist/commands/generate.js +21 -1
  2. package/dist/commands/mcp/serve.js +30 -9
  3. package/dist/lib/generators/base.d.ts +1 -0
  4. package/dist/lib/generators/go.d.ts +2 -1
  5. package/dist/lib/generators/go.js +34 -4
  6. package/dist/lib/generators/python.d.ts +4 -2
  7. package/dist/lib/generators/python.js +28 -6
  8. package/dist/lib/generators/typescript.d.ts +2 -1
  9. package/dist/lib/generators/typescript.js +43 -6
  10. package/dist/lib/guards.d.ts +11 -1
  11. package/dist/lib/guards.js +20 -1
  12. package/dist/lib/templates/go/index-module.eta +6 -0
  13. package/dist/lib/templates/go/middleware/auth.eta +299 -59
  14. package/dist/lib/templates/guards/api-key-auth.guard.eta +146 -0
  15. package/dist/lib/templates/guards/auth.guard.eta +6 -0
  16. package/dist/lib/templates/guards/guards.module.eta +11 -19
  17. package/dist/lib/templates/guards/index.eta +2 -2
  18. package/dist/lib/templates/guards/jwt-auth.guard.eta +172 -0
  19. package/dist/lib/templates/guards/scope.guard.eta +20 -2
  20. package/dist/lib/templates/python/auth.eta +217 -0
  21. package/dist/lib/templates/python/index-module.eta +11 -0
  22. package/dist/lib/templates/python/models/model-col-enum.eta +1 -1
  23. package/dist/lib/templates/python/schemas/schema.eta +22 -1
  24. package/dist/lib/templates/rest/controller-rest.eta +16 -1
  25. package/dist/lib/templates/rest/index-module-rest.eta +6 -0
  26. package/dist/lib/types/auth.d.ts +41 -7
  27. package/dist/lib/types/auth.js +61 -2
  28. package/dist/lib/types/generator.d.ts +6 -0
  29. package/dist/lib/types/index.d.ts +1 -1
  30. package/dist/lib/types/index.js +3 -1
  31. package/dist/lib/utils/crud-deps.d.ts +13 -0
  32. package/dist/lib/utils/crud-deps.js +37 -1
  33. package/npm-shrinkwrap.json +2 -2
  34. package/oclif.manifest.json +1 -1
  35. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isApiKeyAuth = exports.isJwtAuth = exports.isDbSessionAuth = exports.API_KEY_AUTH_DEFAULTS = exports.JWT_AUTH_DEFAULTS = exports.DB_SESSION_AUTH_DEFAULTS = void 0;
3
+ exports.resolveApiKeyAuth = exports.resolveJwtAuth = exports.isApiKeyAuth = exports.isJwtAuth = exports.isDbSessionAuth = exports.API_KEY_AUTH_DEFAULTS = exports.JWT_AUTH_DEFAULTS = exports.DB_SESSION_AUTH_DEFAULTS = void 0;
4
4
  /**
5
5
  * Default values for DB session auth configuration
6
6
  */
@@ -28,6 +28,7 @@ exports.JWT_AUTH_DEFAULTS = {
28
28
  exports.API_KEY_AUTH_DEFAULTS = {
29
29
  apiKeyHeader: "x-api-key",
30
30
  apiKeyEntity: "ApiKey",
31
+ keyField: "keyHash",
31
32
  };
32
33
  /**
33
34
  * Type guard: Check if auth config is DB session based
@@ -46,7 +47,8 @@ exports.isDbSessionAuth = isDbSessionAuth;
46
47
  function isJwtAuth(auth) {
47
48
  return (auth.provider === "auth0" ||
48
49
  auth.provider === "clerk" ||
49
- auth.provider === "cognito");
50
+ auth.provider === "cognito" ||
51
+ auth.provider === "jwt");
50
52
  }
51
53
  exports.isJwtAuth = isJwtAuth;
52
54
  /**
@@ -58,3 +60,60 @@ function isApiKeyAuth(auth) {
58
60
  return auth.provider === "api-key";
59
61
  }
60
62
  exports.isApiKeyAuth = isApiKeyAuth;
63
+ /**
64
+ * Claim defaults per provider. Clerk's v2 session token nests the active
65
+ * organization under `o`; Cognito puts group membership in `cognito:groups`.
66
+ */
67
+ const PROVIDER_CLAIMS = {
68
+ auth0: { userId: "sub", email: "email" },
69
+ clerk: { userId: "sub", email: "email", workspaceId: "o.id", roles: "o.rol" },
70
+ cognito: { userId: "sub", email: "email", roles: "cognito:groups" },
71
+ jwt: { userId: "sub", email: "email", roles: "roles" },
72
+ };
73
+ const ALLOWED_ALGORITHMS = new Set([
74
+ "RS256", "RS384", "RS512", "PS256", "PS384", "PS512", "ES256", "ES384",
75
+ "EdDSA",
76
+ ]);
77
+ /**
78
+ * Fill JWT defaults and reject configs that would generate an unsafe check.
79
+ * Only asymmetric algorithms are accepted: the key comes from the issuer's
80
+ * JWKS, so there is no shared secret to configure or leak.
81
+ */
82
+ function resolveJwtAuth(auth) {
83
+ var _a, _b, _c, _d, _e, _f;
84
+ const issuer = (_b = (_a = auth.jwt) === null || _a === void 0 ? void 0 : _a.issuer) === null || _b === void 0 ? void 0 : _b.trim();
85
+ if (!issuer)
86
+ throw new Error(`auth.jwt.issuer is required for the "${auth.provider}" provider`);
87
+ const algorithms = ((_c = auth.jwt.algorithms) === null || _c === void 0 ? void 0 : _c.length)
88
+ ? auth.jwt.algorithms
89
+ : ["RS256"];
90
+ const bad = algorithms.filter((a) => !ALLOWED_ALGORITHMS.has(a));
91
+ if (bad.length > 0)
92
+ throw new Error(`auth.jwt.algorithms: ${bad.join(", ")} not supported; use ${[
93
+ ...ALLOWED_ALGORITHMS,
94
+ ].join(", ")}`);
95
+ const claims = { ...PROVIDER_CLAIMS[auth.provider], ...auth.claims };
96
+ return {
97
+ provider: auth.provider,
98
+ issuer,
99
+ audience: ((_d = auth.jwt.audience) === null || _d === void 0 ? void 0 : _d.trim()) || undefined,
100
+ jwksUri: ((_e = auth.jwt.jwksUri) === null || _e === void 0 ? void 0 : _e.trim()) ||
101
+ `${issuer.replace(/\/+$/, "")}/.well-known/jwks.json`,
102
+ algorithms,
103
+ authorizedParties: (_f = auth.jwt.authorizedParties) !== null && _f !== void 0 ? _f : [],
104
+ claims: { ...claims, userId: claims.userId || "sub" },
105
+ };
106
+ }
107
+ exports.resolveJwtAuth = resolveJwtAuth;
108
+ /** Fill API key defaults. */
109
+ function resolveApiKeyAuth(auth) {
110
+ return {
111
+ provider: "api-key",
112
+ apiKeyHeader: (auth.apiKeyHeader || exports.API_KEY_AUTH_DEFAULTS.apiKeyHeader).toLowerCase(),
113
+ apiKeyEntity: auth.apiKeyEntity || exports.API_KEY_AUTH_DEFAULTS.apiKeyEntity,
114
+ keyField: auth.keyField || exports.API_KEY_AUTH_DEFAULTS.keyField,
115
+ workspaceField: auth.workspaceField || "organizationId",
116
+ roleField: auth.roleField,
117
+ };
118
+ }
119
+ exports.resolveApiKeyAuth = resolveApiKeyAuth;
@@ -153,6 +153,11 @@ export interface ControllerGenerationOptions extends EntityGenerationOptions {
153
153
  * Relationship map for join queries
154
154
  */
155
155
  relationshipMap: RelationshipMap;
156
+ /**
157
+ * The .apsorc auth config; with scopeBy it limits every query to the
158
+ * caller's tenant
159
+ */
160
+ auth?: AuthConfig;
156
161
  }
157
162
  /**
158
163
  * Options for generating services
@@ -247,6 +252,7 @@ export interface LanguageGenerator {
247
252
  generateIndexModule(entities: Entity[], apiType: string, opts?: {
248
253
  emitEvents?: boolean;
249
254
  http?: boolean;
255
+ auth?: AuthConfig;
250
256
  }): Promise<GeneratedFile[]>;
251
257
  /**
252
258
  * Generate guard files (auth, scope, etc.)
@@ -3,5 +3,5 @@ export { FieldType, Field, ComputedField } from "./field";
3
3
  export { Index } from "./indices";
4
4
  export { JSONValue } from "./json";
5
5
  export { RelationshipType, Association, ApsorcRelationship, Relationship, RelationshipForTemplate, RelationshipMap, } from "./relationship";
6
- export { AuthProviderType, BaseAuthConfig, DbSessionAuthConfig, JwtConfig, JwtClaimsMapping, JwtAuthConfig, ApiKeyAuthConfig, AuthConfig, AuthContext, DB_SESSION_AUTH_DEFAULTS, JWT_AUTH_DEFAULTS, API_KEY_AUTH_DEFAULTS, isDbSessionAuth, isJwtAuth, isApiKeyAuth, } from "./auth";
6
+ export { AuthProviderType, BaseAuthConfig, DbSessionAuthConfig, JwtConfig, JwtClaimsMapping, JwtAuthConfig, ApiKeyAuthConfig, AuthConfig, AuthContext, DB_SESSION_AUTH_DEFAULTS, JWT_AUTH_DEFAULTS, API_KEY_AUTH_DEFAULTS, isDbSessionAuth, isJwtAuth, isApiKeyAuth, ResolvedJwtAuth, ResolvedApiKeyAuth, resolveJwtAuth, resolveApiKeyAuth, } from "./auth";
7
7
  export { TargetLanguage, GeneratedFile, ValidationResult, PythonLanguageConfig, GoLanguageConfig, TypeScriptLanguageConfig, LanguageConfig, GeneratorConfig, EntityGenerationOptions, ControllerGenerationOptions, ServiceGenerationOptions, DtoGenerationOptions, MigrationGenerationOptions, LanguageGenerator, GeneratorFactory, GeneratorRegistry, } from "./generator";
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isApiKeyAuth = exports.isJwtAuth = exports.isDbSessionAuth = exports.API_KEY_AUTH_DEFAULTS = exports.JWT_AUTH_DEFAULTS = exports.DB_SESSION_AUTH_DEFAULTS = void 0;
3
+ exports.resolveApiKeyAuth = exports.resolveJwtAuth = exports.isApiKeyAuth = exports.isJwtAuth = exports.isDbSessionAuth = exports.API_KEY_AUTH_DEFAULTS = exports.JWT_AUTH_DEFAULTS = exports.DB_SESSION_AUTH_DEFAULTS = void 0;
4
4
  var auth_1 = require("./auth");
5
5
  Object.defineProperty(exports, "DB_SESSION_AUTH_DEFAULTS", { enumerable: true, get: function () { return auth_1.DB_SESSION_AUTH_DEFAULTS; } });
6
6
  Object.defineProperty(exports, "JWT_AUTH_DEFAULTS", { enumerable: true, get: function () { return auth_1.JWT_AUTH_DEFAULTS; } });
@@ -8,3 +8,5 @@ Object.defineProperty(exports, "API_KEY_AUTH_DEFAULTS", { enumerable: true, get:
8
8
  Object.defineProperty(exports, "isDbSessionAuth", { enumerable: true, get: function () { return auth_1.isDbSessionAuth; } });
9
9
  Object.defineProperty(exports, "isJwtAuth", { enumerable: true, get: function () { return auth_1.isJwtAuth; } });
10
10
  Object.defineProperty(exports, "isApiKeyAuth", { enumerable: true, get: function () { return auth_1.isApiKeyAuth; } });
11
+ Object.defineProperty(exports, "resolveJwtAuth", { enumerable: true, get: function () { return auth_1.resolveJwtAuth; } });
12
+ Object.defineProperty(exports, "resolveApiKeyAuth", { enumerable: true, get: function () { return auth_1.resolveApiKeyAuth; } });
@@ -24,3 +24,16 @@ export declare function ensureCrudDeps(projectDir: string): string[];
24
24
  * @returns {string[]} Names added (empty when nothing changed or no package.json).
25
25
  */
26
26
  export declare function addMissingDeps(projectDir: string, deps: Record<string, string>): string[];
27
+ /** JWT verification library the generated TypeScript JWT guard imports. */
28
+ export declare const JOSE_DEP: Record<string, string>;
29
+ /** JWT verification library the generated Python auth module imports. */
30
+ export declare const PYJWT_DEP = "PyJWT[crypto]>=2.8.0";
31
+ /**
32
+ * Add a requirement to pyproject.toml's [project] dependencies list unless a
33
+ * requirement for the same package is already there.
34
+ *
35
+ * @param {string} projectDir - Directory holding pyproject.toml.
36
+ * @param {string} requirement - A PEP 508 requirement, e.g. "PyJWT[crypto]>=2.8.0".
37
+ * @returns {boolean} True when pyproject.toml was changed.
38
+ */
39
+ export declare function addMissingPythonDep(projectDir: string, requirement: string): boolean;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.addMissingDeps = exports.ensureCrudDeps = exports.APSO_CRUD_DEPS = void 0;
3
+ exports.addMissingPythonDep = exports.PYJWT_DEP = exports.JOSE_DEP = exports.addMissingDeps = exports.ensureCrudDeps = exports.APSO_CRUD_DEPS = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const fs = tslib_1.__importStar(require("fs"));
6
6
  const path = tslib_1.__importStar(require("path"));
@@ -56,3 +56,39 @@ function addMissingDeps(projectDir, deps) {
56
56
  return missing;
57
57
  }
58
58
  exports.addMissingDeps = addMissingDeps;
59
+ /** JWT verification library the generated TypeScript JWT guard imports. */
60
+ exports.JOSE_DEP = { jose: "^5.10.0" };
61
+ /** JWT verification library the generated Python auth module imports. */
62
+ exports.PYJWT_DEP = "PyJWT[crypto]>=2.8.0";
63
+ /**
64
+ * Add a requirement to pyproject.toml's [project] dependencies list unless a
65
+ * requirement for the same package is already there.
66
+ *
67
+ * @param {string} projectDir - Directory holding pyproject.toml.
68
+ * @param {string} requirement - A PEP 508 requirement, e.g. "PyJWT[crypto]>=2.8.0".
69
+ * @returns {boolean} True when pyproject.toml was changed.
70
+ */
71
+ function addMissingPythonDep(projectDir, requirement) {
72
+ var _a;
73
+ const file = path.join(projectDir, "pyproject.toml");
74
+ if (!fs.existsSync(file))
75
+ return false;
76
+ const text = fs.readFileSync(file, "utf8");
77
+ const name = requirement.split(/[ !<=>[~]/)[0].toLowerCase();
78
+ // The list's closing bracket sits on its own line, or ends a one-line list;
79
+ // brackets inside requirements ("uvicorn[standard]") are not the end.
80
+ const list = (_a = /^dependencies\s*=\s*\[(.*)][^\S\n]*$/m.exec(text)) !== null && _a !== void 0 ? _a : /^dependencies\s*=\s*\[([\S\s]*?)^]/m.exec(text);
81
+ if (!list)
82
+ return false;
83
+ const declared = [...list[1].matchAll(/["']([^"']+)["']/g)].map((m) => m[1].split(/[ !<=>[~]/)[0].toLowerCase());
84
+ if (declared.includes(name))
85
+ return false;
86
+ const body = list[1].replace(/\s*$/, "");
87
+ const sep = body.trim() === "" || body.trim().endsWith(",") ? "" : ",";
88
+ const updated = text.slice(0, list.index) +
89
+ `dependencies = [${body}${sep}\n "${requirement}",\n]` +
90
+ text.slice(list.index + list[0].length);
91
+ fs.writeFileSync(file, updated);
92
+ return true;
93
+ }
94
+ exports.addMissingPythonDep = addMissingPythonDep;
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@apso/cli",
3
- "version": "0.44.0",
3
+ "version": "0.45.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@apso/cli",
9
- "version": "0.44.0",
9
+ "version": "0.45.0",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
12
  "@apso/schema-tools": "^0.2.0",
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.44.0",
2
+ "version": "0.45.0",
3
3
  "commands": {
4
4
  "config": {
5
5
  "id": "config",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apso/cli",
3
- "version": "0.44.0",
3
+ "version": "0.45.0",
4
4
  "mcpName": "io.github.apsoai/apso",
5
5
  "description": "Apso CLI",
6
6
  "author": "Apso by Mavric - @mavric",