mythik-server 0.1.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 (104) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +4 -0
  3. package/README.md +51 -0
  4. package/dist/api-handler.d.ts +4 -0
  5. package/dist/api-handler.d.ts.map +1 -0
  6. package/dist/api-handler.js +110 -0
  7. package/dist/api-handler.js.map +1 -0
  8. package/dist/audit.d.ts +18 -0
  9. package/dist/audit.d.ts.map +1 -0
  10. package/dist/audit.js +42 -0
  11. package/dist/audit.js.map +1 -0
  12. package/dist/auth/db-auth-provider.d.ts +10 -0
  13. package/dist/auth/db-auth-provider.d.ts.map +1 -0
  14. package/dist/auth/db-auth-provider.js +130 -0
  15. package/dist/auth/db-auth-provider.js.map +1 -0
  16. package/dist/auth/jwt-strategy.d.ts +3 -0
  17. package/dist/auth/jwt-strategy.d.ts.map +1 -0
  18. package/dist/auth/jwt-strategy.js +39 -0
  19. package/dist/auth/jwt-strategy.js.map +1 -0
  20. package/dist/auth/middleware.d.ts +4 -0
  21. package/dist/auth/middleware.d.ts.map +1 -0
  22. package/dist/auth/middleware.js +40 -0
  23. package/dist/auth/middleware.js.map +1 -0
  24. package/dist/auth/password-verifier.d.ts +3 -0
  25. package/dist/auth/password-verifier.d.ts.map +1 -0
  26. package/dist/auth/password-verifier.js +18 -0
  27. package/dist/auth/password-verifier.js.map +1 -0
  28. package/dist/auth/policy-evaluator.d.ts +3 -0
  29. package/dist/auth/policy-evaluator.d.ts.map +1 -0
  30. package/dist/auth/policy-evaluator.js +11 -0
  31. package/dist/auth/policy-evaluator.js.map +1 -0
  32. package/dist/auth/refresh-store.d.ts +8 -0
  33. package/dist/auth/refresh-store.d.ts.map +1 -0
  34. package/dist/auth/refresh-store.js +34 -0
  35. package/dist/auth/refresh-store.js.map +1 -0
  36. package/dist/auth/scope-filter.d.ts +10 -0
  37. package/dist/auth/scope-filter.d.ts.map +1 -0
  38. package/dist/auth/scope-filter.js +51 -0
  39. package/dist/auth/scope-filter.js.map +1 -0
  40. package/dist/auth/types.d.ts +75 -0
  41. package/dist/auth/types.d.ts.map +1 -0
  42. package/dist/auth/types.js +3 -0
  43. package/dist/auth/types.js.map +1 -0
  44. package/dist/auth/user-context.d.ts +4 -0
  45. package/dist/auth/user-context.d.ts.map +1 -0
  46. package/dist/auth/user-context.js +18 -0
  47. package/dist/auth/user-context.js.map +1 -0
  48. package/dist/catalog-builder.d.ts +3 -0
  49. package/dist/catalog-builder.d.ts.map +1 -0
  50. package/dist/catalog-builder.js +35 -0
  51. package/dist/catalog-builder.js.map +1 -0
  52. package/dist/connection.d.ts +4 -0
  53. package/dist/connection.d.ts.map +1 -0
  54. package/dist/connection.js +18 -0
  55. package/dist/connection.js.map +1 -0
  56. package/dist/crud-builder.d.ts +14 -0
  57. package/dist/crud-builder.d.ts.map +1 -0
  58. package/dist/crud-builder.js +43 -0
  59. package/dist/crud-builder.js.map +1 -0
  60. package/dist/handler-loader.d.ts +9 -0
  61. package/dist/handler-loader.d.ts.map +1 -0
  62. package/dist/handler-loader.js +42 -0
  63. package/dist/handler-loader.js.map +1 -0
  64. package/dist/index.d.ts +15 -0
  65. package/dist/index.d.ts.map +1 -0
  66. package/dist/index.js +14 -0
  67. package/dist/index.js.map +1 -0
  68. package/dist/middleware/cors.d.ts +3 -0
  69. package/dist/middleware/cors.d.ts.map +1 -0
  70. package/dist/middleware/cors.js +8 -0
  71. package/dist/middleware/cors.js.map +1 -0
  72. package/dist/middleware/error-handler.d.ts +9 -0
  73. package/dist/middleware/error-handler.d.ts.map +1 -0
  74. package/dist/middleware/error-handler.js +56 -0
  75. package/dist/middleware/error-handler.js.map +1 -0
  76. package/dist/query-engine.d.ts +8 -0
  77. package/dist/query-engine.d.ts.map +1 -0
  78. package/dist/query-engine.js +66 -0
  79. package/dist/query-engine.js.map +1 -0
  80. package/dist/server.d.ts +3 -0
  81. package/dist/server.d.ts.map +1 -0
  82. package/dist/server.js +717 -0
  83. package/dist/server.js.map +1 -0
  84. package/dist/spec-loader.d.ts +6 -0
  85. package/dist/spec-loader.d.ts.map +1 -0
  86. package/dist/spec-loader.js +21 -0
  87. package/dist/spec-loader.js.map +1 -0
  88. package/dist/spec-serving.d.ts +9 -0
  89. package/dist/spec-serving.d.ts.map +1 -0
  90. package/dist/spec-serving.js +53 -0
  91. package/dist/spec-serving.js.map +1 -0
  92. package/dist/types.d.ts +131 -0
  93. package/dist/types.d.ts.map +1 -0
  94. package/dist/types.js +2 -0
  95. package/dist/types.js.map +1 -0
  96. package/dist/validation/identifier-guard.d.ts +2 -0
  97. package/dist/validation/identifier-guard.d.ts.map +1 -0
  98. package/dist/validation/identifier-guard.js +3 -0
  99. package/dist/validation/identifier-guard.js.map +1 -0
  100. package/dist/validation/spec-validator.d.ts +3 -0
  101. package/dist/validation/spec-validator.d.ts.map +1 -0
  102. package/dist/validation/spec-validator.js +3 -0
  103. package/dist/validation/spec-validator.js.map +1 -0
  104. package/package.json +63 -0
@@ -0,0 +1,39 @@
1
+ import jwt from 'jsonwebtoken';
2
+ export function createJwtStrategy(config) {
3
+ const secret = config.secret;
4
+ function extractToken(req) {
5
+ const auth = req.headers.authorization ?? req.headers.Authorization;
6
+ if (typeof auth !== 'string')
7
+ return null;
8
+ if (!auth.startsWith('Bearer '))
9
+ return null;
10
+ const token = auth.slice(7).trim();
11
+ return token.length > 0 ? token : null;
12
+ }
13
+ async function validateToken(token) {
14
+ return new Promise((resolve, reject) => {
15
+ const options = {};
16
+ if (config.issuer)
17
+ options.issuer = config.issuer;
18
+ if (config.audience)
19
+ options.audience = config.audience;
20
+ jwt.verify(token, secret, options, (err, decoded) => {
21
+ if (err)
22
+ return reject(err);
23
+ resolve(decoded);
24
+ });
25
+ });
26
+ }
27
+ function generateToken(payload, expiresInMinutes) {
28
+ const options = {
29
+ expiresIn: expiresInMinutes * 60,
30
+ };
31
+ if (config.issuer)
32
+ options.issuer = config.issuer;
33
+ if (config.audience)
34
+ options.audience = config.audience;
35
+ return jwt.sign(payload, secret, options);
36
+ }
37
+ return { extractToken, validateToken, generateToken };
38
+ }
39
+ //# sourceMappingURL=jwt-strategy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt-strategy.js","sourceRoot":"","sources":["../../src/auth/jwt-strategy.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,cAAc,CAAC;AAG/B,MAAM,UAAU,iBAAiB,CAAC,MAAiB;IACjD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IAE7B,SAAS,YAAY,CAAC,GAA+D;QACnF,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,IAAI,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC;QACpE,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QAC1C,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;YAAE,OAAO,IAAI,CAAC;QAC7C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACnC,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IACzC,CAAC;IAED,KAAK,UAAU,aAAa,CAAC,KAAa;QACxC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,OAAO,GAAsB,EAAE,CAAC;YACtC,IAAI,MAAM,CAAC,MAAM;gBAAE,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;YAClD,IAAI,MAAM,CAAC,QAAQ;gBAAE,OAAO,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;YAExD,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE;gBAClD,IAAI,GAAG;oBAAE,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;gBAC5B,OAAO,CAAC,OAAkC,CAAC,CAAC;YAC9C,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED,SAAS,aAAa,CAAC,OAAgC,EAAE,gBAAwB;QAC/E,MAAM,OAAO,GAAoB;YAC/B,SAAS,EAAE,gBAAgB,GAAG,EAAE;SACjC,CAAC;QACF,IAAI,MAAM,CAAC,MAAM;YAAE,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;QAClD,IAAI,MAAM,CAAC,QAAQ;YAAE,OAAO,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;QAExD,OAAO,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5C,CAAC;IAED,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,CAAC;AACxD,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { Request, Response, NextFunction } from 'express';
2
+ import type { AuthConfig } from './types.js';
3
+ export declare function createAuthMiddleware(authConfig: AuthConfig): (policy?: string) => (req: Request, res: Response, next: NextFunction) => Promise<void>;
4
+ //# sourceMappingURL=middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/auth/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAM7C,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,UAAU,IAKnB,SAAS,MAAM,MACd,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,KAAG,OAAO,CAAC,IAAI,CAAC,CAiCvG"}
@@ -0,0 +1,40 @@
1
+ import { createJwtStrategy } from './jwt-strategy.js';
2
+ import { buildUserContext } from './user-context.js';
3
+ import { evaluatePolicy } from './policy-evaluator.js';
4
+ export function createAuthMiddleware(authConfig) {
5
+ const jwtStrategy = createJwtStrategy(authConfig.jwt);
6
+ const policies = authConfig.policies ?? {};
7
+ const claimsMapping = authConfig.jwt.claims ?? {};
8
+ return function authMiddlewareFactory(policy) {
9
+ return async function authMiddleware(req, res, next) {
10
+ // Public endpoints skip auth
11
+ if (policy === 'public')
12
+ return next();
13
+ // Extract token
14
+ const token = jwtStrategy.extractToken(req);
15
+ if (!token) {
16
+ res.status(401).json({ error: { code: 'TOKEN_REQUIRED', message: 'Authentication required' } });
17
+ return;
18
+ }
19
+ // Validate token
20
+ let claims;
21
+ try {
22
+ claims = await jwtStrategy.validateToken(token);
23
+ }
24
+ catch {
25
+ res.status(401).json({ error: { code: 'TOKEN_INVALID', message: 'Invalid or expired token' } });
26
+ return;
27
+ }
28
+ // Build user context
29
+ const user = buildUserContext(claims, claimsMapping);
30
+ req.user = user;
31
+ // Evaluate policy
32
+ if (!evaluatePolicy(policy, user.roles, policies)) {
33
+ res.status(403).json({ error: { code: 'FORBIDDEN', message: 'Insufficient permissions' } });
34
+ return;
35
+ }
36
+ next();
37
+ };
38
+ };
39
+ }
40
+ //# sourceMappingURL=middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/auth/middleware.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,MAAM,UAAU,oBAAoB,CAAC,UAAsB;IACzD,MAAM,WAAW,GAAG,iBAAiB,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IACtD,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,IAAI,EAAE,CAAC;IAC3C,MAAM,aAAa,GAAG,UAAU,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC;IAElD,OAAO,SAAS,qBAAqB,CAAC,MAAe;QACnD,OAAO,KAAK,UAAU,cAAc,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB;YAClF,6BAA6B;YAC7B,IAAI,MAAM,KAAK,QAAQ;gBAAE,OAAO,IAAI,EAAE,CAAC;YAEvC,gBAAgB;YAChB,MAAM,KAAK,GAAG,WAAW,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;YAC5C,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,yBAAyB,EAAE,EAAE,CAAC,CAAC;gBAChG,OAAO;YACT,CAAC;YAED,iBAAiB;YACjB,IAAI,MAA+B,CAAC;YACpC,IAAI,CAAC;gBACH,MAAM,GAAG,MAAM,WAAW,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;YAClD,CAAC;YAAC,MAAM,CAAC;gBACP,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,0BAA0B,EAAE,EAAE,CAAC,CAAC;gBAChG,OAAO;YACT,CAAC;YAED,qBAAqB;YACrB,MAAM,IAAI,GAAgB,gBAAgB,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;YACjE,GAA0C,CAAC,IAAI,GAAG,IAAI,CAAC;YAExD,kBAAkB;YAClB,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,EAAE,CAAC;gBAClD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,0BAA0B,EAAE,EAAE,CAAC,CAAC;gBAC5F,OAAO;YACT,CAAC;YAED,IAAI,EAAE,CAAC;QACT,CAAC,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { PasswordVerifier } from './types.js';
2
+ export declare function getPasswordVerifier(algorithm: string | undefined): PasswordVerifier;
3
+ //# sourceMappingURL=password-verifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"password-verifier.d.ts","sourceRoot":"","sources":["../../src/auth/password-verifier.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAYnD,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,gBAAgB,CAOnF"}
@@ -0,0 +1,18 @@
1
+ import bcrypt from 'bcryptjs';
2
+ const bcryptVerifier = {
3
+ async verify(password, hash) {
4
+ return bcrypt.compare(password, hash);
5
+ },
6
+ };
7
+ const verifiers = {
8
+ bcrypt: bcryptVerifier,
9
+ };
10
+ export function getPasswordVerifier(algorithm) {
11
+ const name = algorithm ?? 'bcrypt';
12
+ const verifier = verifiers[name];
13
+ if (!verifier) {
14
+ throw new Error(`Unsupported password hash algorithm: "${name}". Supported: ${Object.keys(verifiers).join(', ')}`);
15
+ }
16
+ return verifier;
17
+ }
18
+ //# sourceMappingURL=password-verifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"password-verifier.js","sourceRoot":"","sources":["../../src/auth/password-verifier.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,UAAU,CAAC;AAG9B,MAAM,cAAc,GAAqB;IACvC,KAAK,CAAC,MAAM,CAAC,QAAgB,EAAE,IAAY;QACzC,OAAO,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IACxC,CAAC;CACF,CAAC;AAEF,MAAM,SAAS,GAAqC;IAClD,MAAM,EAAE,cAAc;CACvB,CAAC;AAEF,MAAM,UAAU,mBAAmB,CAAC,SAA6B;IAC/D,MAAM,IAAI,GAAG,SAAS,IAAI,QAAQ,CAAC;IACnC,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IACjC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,yCAAyC,IAAI,iBAAiB,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACrH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { PolicyConfig } from './types.js';
2
+ export declare function evaluatePolicy(policy: string | undefined, userRoles: string[], policies: Record<string, PolicyConfig>): boolean;
3
+ //# sourceMappingURL=policy-evaluator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy-evaluator.d.ts","sourceRoot":"","sources":["../../src/auth/policy-evaluator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,SAAS,EAAE,MAAM,EAAE,EACnB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,GACrC,OAAO,CAQT"}
@@ -0,0 +1,11 @@
1
+ export function evaluatePolicy(policy, userRoles, policies) {
2
+ if (policy === 'public')
3
+ return true;
4
+ if (!policy || policy === 'authenticated')
5
+ return true;
6
+ const policyConfig = policies[policy];
7
+ if (!policyConfig)
8
+ return false;
9
+ return policyConfig.roles.some(role => userRoles.includes(role));
10
+ }
11
+ //# sourceMappingURL=policy-evaluator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy-evaluator.js","sourceRoot":"","sources":["../../src/auth/policy-evaluator.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,cAAc,CAC5B,MAA0B,EAC1B,SAAmB,EACnB,QAAsC;IAEtC,IAAI,MAAM,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACrC,IAAI,CAAC,MAAM,IAAI,MAAM,KAAK,eAAe;QAAE,OAAO,IAAI,CAAC;IAEvD,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;IACtC,IAAI,CAAC,YAAY;QAAE,OAAO,KAAK,CAAC;IAEhC,OAAO,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;AACnE,CAAC"}
@@ -0,0 +1,8 @@
1
+ export interface RefreshStore {
2
+ store(username: string, token: string): void;
3
+ getUsername(token: string): string | null;
4
+ revoke(token: string): void;
5
+ generateToken(): string;
6
+ }
7
+ export declare function createRefreshStore(ttlMinutes: number): RefreshStore;
8
+ //# sourceMappingURL=refresh-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refresh-store.d.ts","sourceRoot":"","sources":["../../src/auth/refresh-store.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7C,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC1C,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,aAAa,IAAI,MAAM,CAAC;CACzB;AAED,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,YAAY,CAmCnE"}
@@ -0,0 +1,34 @@
1
+ import { randomBytes } from 'crypto';
2
+ export function createRefreshStore(ttlMinutes) {
3
+ const tokens = new Map();
4
+ const ttlMs = ttlMinutes * 60 * 1000;
5
+ function cleanExpired() {
6
+ const now = Date.now();
7
+ for (const [token, entry] of tokens) {
8
+ if (entry.expiry <= now)
9
+ tokens.delete(token);
10
+ }
11
+ }
12
+ function store(username, token) {
13
+ tokens.set(token, { username, expiry: Date.now() + ttlMs });
14
+ cleanExpired();
15
+ }
16
+ function getUsername(token) {
17
+ const entry = tokens.get(token);
18
+ if (!entry)
19
+ return null;
20
+ if (entry.expiry <= Date.now()) {
21
+ tokens.delete(token);
22
+ return null;
23
+ }
24
+ return entry.username;
25
+ }
26
+ function revoke(token) {
27
+ tokens.delete(token);
28
+ }
29
+ function generateToken() {
30
+ return randomBytes(64).toString('base64url');
31
+ }
32
+ return { store, getUsername, revoke, generateToken };
33
+ }
34
+ //# sourceMappingURL=refresh-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refresh-store.js","sourceRoot":"","sources":["../../src/auth/refresh-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAcrC,MAAM,UAAU,kBAAkB,CAAC,UAAkB;IACnD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC9C,MAAM,KAAK,GAAG,UAAU,GAAG,EAAE,GAAG,IAAI,CAAC;IAErC,SAAS,YAAY;QACnB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,MAAM,EAAE,CAAC;YACpC,IAAI,KAAK,CAAC,MAAM,IAAI,GAAG;gBAAE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;IAED,SAAS,KAAK,CAAC,QAAgB,EAAE,KAAa;QAC5C,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC;QAC5D,YAAY,EAAE,CAAC;IACjB,CAAC;IAED,SAAS,WAAW,CAAC,KAAa;QAChC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAChC,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QACxB,IAAI,KAAK,CAAC,MAAM,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YAC/B,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACrB,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,KAAK,CAAC,QAAQ,CAAC;IACxB,CAAC;IAED,SAAS,MAAM,CAAC,KAAa;QAC3B,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC;IAED,SAAS,aAAa;QACpB,OAAO,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IAC/C,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;AACvD,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { ScopeFilterConfig } from './types.js';
2
+ export interface ScopeClause {
3
+ sql: string;
4
+ params: Record<string, unknown>;
5
+ }
6
+ export declare function resolveActiveScope(headerValue: string | undefined, type: 'int' | 'string' | undefined): unknown | null;
7
+ export declare function buildScopeWhereClause(config: ScopeFilterConfig, userScope: unknown[], activeScope: unknown | undefined, userRoles: string[], columnOverride?: string): ScopeClause | null;
8
+ export declare function validateScopeForInsert(config: ScopeFilterConfig, body: Record<string, unknown>, userScope: unknown[], userRoles: string[]): boolean;
9
+ export declare function wrapQueryWithScopeFilter(originalSql: string, scopeClause: ScopeClause): string;
10
+ //# sourceMappingURL=scope-filter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope-filter.d.ts","sourceRoot":"","sources":["../../src/auth/scope-filter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAGpD,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,SAAS,GACjC,OAAO,GAAG,IAAI,CAKhB;AAMD,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,iBAAiB,EACzB,SAAS,EAAE,OAAO,EAAE,EACpB,WAAW,EAAE,OAAO,GAAG,SAAS,EAChC,SAAS,EAAE,MAAM,EAAE,EACnB,cAAc,CAAC,EAAE,MAAM,GACtB,WAAW,GAAG,IAAI,CA4BpB;AAED,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,iBAAiB,EACzB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,SAAS,EAAE,OAAO,EAAE,EACpB,SAAS,EAAE,MAAM,EAAE,GAClB,OAAO,CAST;AAED,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,WAAW,GACvB,MAAM,CAER"}
@@ -0,0 +1,51 @@
1
+ import { assertValidIdentifier } from 'mythik';
2
+ export function resolveActiveScope(headerValue, type) {
3
+ if (headerValue === undefined || headerValue === '')
4
+ return null;
5
+ if (type === 'string')
6
+ return headerValue;
7
+ const parsed = parseInt(headerValue, 10);
8
+ return isNaN(parsed) ? null : parsed;
9
+ }
10
+ function hasBypassRole(bypassRoles, userRoles) {
11
+ return bypassRoles.some(role => userRoles.includes(role));
12
+ }
13
+ export function buildScopeWhereClause(config, userScope, activeScope, userRoles, columnOverride) {
14
+ const bypassRoles = config.bypassRoles ?? [];
15
+ if (hasBypassRole(bypassRoles, userRoles))
16
+ return null;
17
+ const column = columnOverride ?? config.column;
18
+ assertValidIdentifier(column, 'scopeFilter.column');
19
+ const mode = config.mode ?? 'all';
20
+ if (mode === 'select') {
21
+ return {
22
+ sql: `_scoped.${column} = @_activeScope`,
23
+ params: { _activeScope: activeScope },
24
+ };
25
+ }
26
+ // mode "all"
27
+ if (userScope.length === 0) {
28
+ return { sql: '1 = 0', params: {} };
29
+ }
30
+ const paramNames = userScope.map((_, i) => `@_scope${i}`);
31
+ const params = {};
32
+ userScope.forEach((val, i) => { params[`_scope${i}`] = val; });
33
+ return {
34
+ sql: `_scoped.${column} IN (${paramNames.join(', ')})`,
35
+ params,
36
+ };
37
+ }
38
+ export function validateScopeForInsert(config, body, userScope, userRoles) {
39
+ const bypassRoles = config.bypassRoles ?? [];
40
+ if (hasBypassRole(bypassRoles, userRoles))
41
+ return true;
42
+ const column = config.column;
43
+ const bodyValue = body[column];
44
+ if (bodyValue === undefined || bodyValue === null)
45
+ return false;
46
+ return userScope.includes(bodyValue);
47
+ }
48
+ export function wrapQueryWithScopeFilter(originalSql, scopeClause) {
49
+ return `SELECT * FROM (\n${originalSql}\n) AS _scoped\nWHERE ${scopeClause.sql}`;
50
+ }
51
+ //# sourceMappingURL=scope-filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope-filter.js","sourceRoot":"","sources":["../../src/auth/scope-filter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,QAAQ,CAAC;AAO/C,MAAM,UAAU,kBAAkB,CAChC,WAA+B,EAC/B,IAAkC;IAElC,IAAI,WAAW,KAAK,SAAS,IAAI,WAAW,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IACjE,IAAI,IAAI,KAAK,QAAQ;QAAE,OAAO,WAAW,CAAC;IAC1C,MAAM,MAAM,GAAG,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IACzC,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC;AACvC,CAAC;AAED,SAAS,aAAa,CAAC,WAAqB,EAAE,SAAmB;IAC/D,OAAO,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,MAAyB,EACzB,SAAoB,EACpB,WAAgC,EAChC,SAAmB,EACnB,cAAuB;IAEvB,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC;IAC7C,IAAI,aAAa,CAAC,WAAW,EAAE,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IAEvD,MAAM,MAAM,GAAG,cAAc,IAAI,MAAM,CAAC,MAAM,CAAC;IAC/C,qBAAqB,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,KAAK,CAAC;IAElC,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtB,OAAO;YACL,GAAG,EAAE,WAAW,MAAM,kBAAkB;YACxC,MAAM,EAAE,EAAE,YAAY,EAAE,WAAW,EAAE;SACtC,CAAC;IACJ,CAAC;IAED,aAAa;IACb,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IACtC,CAAC;IAED,MAAM,UAAU,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IAC1D,MAAM,MAAM,GAA4B,EAAE,CAAC;IAC3C,SAAS,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,GAAG,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAE/D,OAAO;QACL,GAAG,EAAE,WAAW,MAAM,QAAQ,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;QACtD,MAAM;KACP,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,MAAyB,EACzB,IAA6B,EAC7B,SAAoB,EACpB,SAAmB;IAEnB,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC;IAC7C,IAAI,aAAa,CAAC,WAAW,EAAE,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IAEvD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IAC7B,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;IAC/B,IAAI,SAAS,KAAK,SAAS,IAAI,SAAS,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAEhE,OAAO,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,wBAAwB,CACtC,WAAmB,EACnB,WAAwB;IAExB,OAAO,oBAAoB,WAAW,yBAAyB,WAAW,CAAC,GAAG,EAAE,CAAC;AACnF,CAAC"}
@@ -0,0 +1,75 @@
1
+ export interface AuthStrategy {
2
+ extractToken(req: {
3
+ headers: Record<string, string | string[] | undefined>;
4
+ }): string | null;
5
+ validateToken(token: string): Promise<Record<string, unknown>>;
6
+ generateToken(payload: Record<string, unknown>, expiresInMinutes: number): string;
7
+ }
8
+ export interface PasswordVerifier {
9
+ verify(password: string, hash: string): Promise<boolean>;
10
+ }
11
+ export interface JwtConfig {
12
+ secret: string;
13
+ issuer?: string;
14
+ audience?: string;
15
+ expiresIn?: number;
16
+ refreshExpiresIn?: number;
17
+ claims?: ClaimsMapping;
18
+ }
19
+ export interface ClaimsMapping {
20
+ username?: string;
21
+ name?: string;
22
+ roles?: string;
23
+ scope?: string;
24
+ }
25
+ export interface ProviderConfig {
26
+ usersTable: string;
27
+ usernameColumn: string;
28
+ passwordColumn: string;
29
+ passwordHash?: 'bcrypt' | 'argon2';
30
+ activeCondition?: string;
31
+ emailColumn?: string;
32
+ displayNameQuery?: string;
33
+ rolesQuery: string;
34
+ scopeQuery: string;
35
+ defaultScopeQuery?: string;
36
+ }
37
+ export interface PolicyConfig {
38
+ roles: string[];
39
+ }
40
+ export interface ScopeFilterConfig {
41
+ claim: string;
42
+ type?: 'int' | 'string';
43
+ column: string;
44
+ bypassRoles?: string[];
45
+ mode?: 'all' | 'select';
46
+ header?: string;
47
+ }
48
+ export interface EndpointScopeOverride {
49
+ column: string;
50
+ }
51
+ export interface AuthConfig {
52
+ strategy: 'jwt';
53
+ jwt: JwtConfig;
54
+ provider?: ProviderConfig;
55
+ policies?: Record<string, PolicyConfig>;
56
+ scopeFilter?: ScopeFilterConfig;
57
+ catalogsPolicy?: 'public';
58
+ }
59
+ export interface LoginResponseUser {
60
+ id: string;
61
+ email: string;
62
+ name: string;
63
+ role: string;
64
+ roles: string[];
65
+ scope: unknown[];
66
+ defaultScope: unknown;
67
+ metadata: Record<string, unknown>;
68
+ }
69
+ export interface LoginResponse {
70
+ token: string;
71
+ refreshToken: string;
72
+ expiresIn: number;
73
+ user: LoginResponseUser;
74
+ }
75
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/auth/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ // --- Auth Strategy (extensible — only JWT implemented) ---
2
+ export {};
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/auth/types.ts"],"names":[],"mappings":"AAAA,4DAA4D"}
@@ -0,0 +1,4 @@
1
+ import type { UserContext } from '../types.js';
2
+ import type { ClaimsMapping } from './types.js';
3
+ export declare function buildUserContext(claims: Record<string, unknown>, mapping: ClaimsMapping): UserContext;
4
+ //# sourceMappingURL=user-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-context.d.ts","sourceRoot":"","sources":["../../src/auth/user-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,aAAa,GAAG,WAAW,CAoBrG"}
@@ -0,0 +1,18 @@
1
+ export function buildUserContext(claims, mapping) {
2
+ const usernameKey = mapping.username ?? 'sub';
3
+ const nameKey = mapping.name;
4
+ const rolesKey = mapping.roles ?? 'roles';
5
+ const scopeKey = mapping.scope;
6
+ const username = String(claims[usernameKey] ?? '');
7
+ const name = nameKey ? claims[nameKey] ?? null : null;
8
+ const rolesRaw = claims[rolesKey];
9
+ const roles = Array.isArray(rolesRaw)
10
+ ? rolesRaw
11
+ : typeof rolesRaw === 'string'
12
+ ? [rolesRaw]
13
+ : [];
14
+ const scopeRaw = scopeKey ? claims[scopeKey] : undefined;
15
+ const scope = Array.isArray(scopeRaw) ? scopeRaw : [];
16
+ return { username, name, roles, scope, raw: claims };
17
+ }
18
+ //# sourceMappingURL=user-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-context.js","sourceRoot":"","sources":["../../src/auth/user-context.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,gBAAgB,CAAC,MAA+B,EAAE,OAAsB;IACtF,MAAM,WAAW,GAAG,OAAO,CAAC,QAAQ,IAAI,KAAK,CAAC;IAC9C,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAC7B,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC;IAC1C,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC;IAE/B,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC;IACnD,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAE,MAAM,CAAC,OAAO,CAAmB,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IAEzE,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IAClC,MAAM,KAAK,GAAa,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC;QAC7C,CAAC,CAAC,QAAQ;QACV,CAAC,CAAC,OAAO,QAAQ,KAAK,QAAQ;YAC5B,CAAC,CAAC,CAAC,QAAQ,CAAC;YACZ,CAAC,CAAC,EAAE,CAAC;IAET,MAAM,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACzD,MAAM,KAAK,GAAc,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;IAEjE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;AACvD,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { CatalogConfig } from './types.js';
2
+ export declare function buildCatalogQuery(config: CatalogConfig): string | null;
3
+ //# sourceMappingURL=catalog-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog-builder.d.ts","sourceRoot":"","sources":["../src/catalog-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAGhD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CAiCtE"}
@@ -0,0 +1,35 @@
1
+ import { assertValidIdentifier } from './validation/identifier-guard.js';
2
+ export function buildCatalogQuery(config) {
3
+ if (config.static)
4
+ return null;
5
+ if (!config.from)
6
+ return null;
7
+ assertValidIdentifier(config.from, 'catalog.from');
8
+ if (config.distinct) {
9
+ assertValidIdentifier(config.distinct, 'catalog.distinct');
10
+ const orderByClause = config.orderBy
11
+ ? `ORDER BY ${bracketOrderBy(config.orderBy)}`
12
+ : `ORDER BY [${config.distinct}] ASC`;
13
+ return `SELECT DISTINCT [${config.distinct}] AS [value], [${config.distinct}] AS [label] FROM [${config.from}] ${orderByClause}`;
14
+ }
15
+ assertValidIdentifier(config.value, 'catalog.value');
16
+ assertValidIdentifier(config.label, 'catalog.label');
17
+ const selectParts = [`[${config.value}] AS [value]`, `[${config.label}] AS [label]`];
18
+ if (config.extra) {
19
+ for (const field of config.extra) {
20
+ assertValidIdentifier(field, 'catalog.extra');
21
+ selectParts.push(`[${field}]`);
22
+ }
23
+ }
24
+ const whereClause = config.where ? ` WHERE ${config.where}` : '';
25
+ const orderByClause = config.orderBy
26
+ ? `ORDER BY ${bracketOrderBy(config.orderBy)}`
27
+ : `ORDER BY [${config.label}] ASC`;
28
+ return `SELECT ${selectParts.join(', ')} FROM [${config.from}]${whereClause} ${orderByClause}`;
29
+ }
30
+ function bracketOrderBy(orderBy) {
31
+ return orderBy.replace(/^(\w+)(\s+(?:ASC|DESC))?$/i, (_match, col, dir) => {
32
+ return `[${col}]${dir ?? ''}`;
33
+ });
34
+ }
35
+ //# sourceMappingURL=catalog-builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog-builder.js","sourceRoot":"","sources":["../src/catalog-builder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AAEzE,MAAM,UAAU,iBAAiB,CAAC,MAAqB;IACrD,IAAI,MAAM,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAE/B,IAAI,CAAC,MAAM,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IAE9B,qBAAqB,CAAC,MAAM,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;IAEnD,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACpB,qBAAqB,CAAC,MAAM,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC;QAC3D,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO;YAClC,CAAC,CAAC,YAAY,cAAc,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE;YAC9C,CAAC,CAAC,aAAa,MAAM,CAAC,QAAQ,OAAO,CAAC;QACxC,OAAO,oBAAoB,MAAM,CAAC,QAAQ,kBAAkB,MAAM,CAAC,QAAQ,sBAAsB,MAAM,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;IACnI,CAAC;IAED,qBAAqB,CAAC,MAAM,CAAC,KAAM,EAAE,eAAe,CAAC,CAAC;IACtD,qBAAqB,CAAC,MAAM,CAAC,KAAM,EAAE,eAAe,CAAC,CAAC;IAEtD,MAAM,WAAW,GAAG,CAAC,IAAI,MAAM,CAAC,KAAK,cAAc,EAAE,IAAI,MAAM,CAAC,KAAK,cAAc,CAAC,CAAC;IAErF,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YACjC,qBAAqB,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC;YAC9C,WAAW,CAAC,IAAI,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC;QACjC,CAAC;IACH,CAAC;IAED,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACjE,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO;QAClC,CAAC,CAAC,YAAY,cAAc,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE;QAC9C,CAAC,CAAC,aAAa,MAAM,CAAC,KAAK,OAAO,CAAC;IAErC,OAAO,UAAU,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,MAAM,CAAC,IAAI,IAAI,WAAW,IAAI,aAAa,EAAE,CAAC;AACjG,CAAC;AAED,SAAS,cAAc,CAAC,OAAe;IACrC,OAAO,OAAO,CAAC,OAAO,CAAC,4BAA4B,EAAE,CAAC,MAAM,EAAE,GAAW,EAAE,GAAuB,EAAE,EAAE;QACpG,OAAO,IAAI,GAAG,IAAI,GAAG,IAAI,EAAE,EAAE,CAAC;IAChC,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { type ConnectionPool } from 'mssql';
2
+ import type { ConnectionConfig } from './types.js';
3
+ export declare function createConnectionPool(config: ConnectionConfig): Promise<ConnectionPool>;
4
+ //# sourceMappingURL=connection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"AAAA,OAAY,EAAE,KAAK,cAAc,EAAE,MAAM,OAAO,CAAC;AACjD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGnD,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,cAAc,CAAC,CAgB5F"}
@@ -0,0 +1,18 @@
1
+ import sql from 'mssql';
2
+ import { resolveEnvVars } from './spec-loader.js';
3
+ export async function createConnectionPool(config) {
4
+ const resolved = resolveEnvVars(config);
5
+ const pool = new sql.ConnectionPool({
6
+ server: resolved.server,
7
+ database: resolved.database,
8
+ user: resolved.user,
9
+ password: resolved.password,
10
+ port: resolved.port ?? 1433,
11
+ options: {
12
+ trustServerCertificate: resolved.trustServerCertificate ?? true,
13
+ },
14
+ });
15
+ await pool.connect();
16
+ return pool;
17
+ }
18
+ //# sourceMappingURL=connection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.js","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"AAAA,OAAO,GAA4B,MAAM,OAAO,CAAC;AAEjD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,MAAwB;IACjE,MAAM,QAAQ,GAAG,cAAc,CAAC,MAA4C,CAAC,CAAC;IAE9E,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,cAAc,CAAC;QAClC,MAAM,EAAE,QAAQ,CAAC,MAAgB;QACjC,QAAQ,EAAE,QAAQ,CAAC,QAAkB;QACrC,IAAI,EAAE,QAAQ,CAAC,IAA0B;QACzC,QAAQ,EAAE,QAAQ,CAAC,QAA8B;QACjD,IAAI,EAAG,QAAQ,CAAC,IAAe,IAAI,IAAI;QACvC,OAAO,EAAE;YACP,sBAAsB,EAAG,QAAQ,CAAC,sBAAkC,IAAI,IAAI;SAC7E;KACF,CAAC,CAAC;IAEH,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;IACrB,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,14 @@
1
+ export declare function filterFields(body: Record<string, unknown>, allowedFields: string[]): Record<string, unknown>;
2
+ export declare function buildInsertQuery(table: string, fields: Record<string, unknown>, primaryKey?: string): {
3
+ sql: string;
4
+ params: Record<string, unknown>;
5
+ };
6
+ export declare function buildUpdateQuery(table: string, primaryKey: string, pkValue: unknown, fields: Record<string, unknown>): {
7
+ sql: string;
8
+ params: Record<string, unknown>;
9
+ };
10
+ export declare function buildDeleteQuery(table: string, primaryKey: string, pkValue: unknown): {
11
+ sql: string;
12
+ params: Record<string, unknown>;
13
+ };
14
+ //# sourceMappingURL=crud-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crud-builder.d.ts","sourceRoot":"","sources":["../src/crud-builder.ts"],"names":[],"mappings":"AAEA,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAS5G;AAED,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,UAAU,CAAC,EAAE,MAAM,GAClB;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAelD;AAED,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAUlD;AAED,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,OAAO,GACf;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAKlD"}
@@ -0,0 +1,43 @@
1
+ import { assertValidIdentifier } from './validation/identifier-guard.js';
2
+ export function filterFields(body, allowedFields) {
3
+ const filtered = {};
4
+ const allowed = new Set(allowedFields);
5
+ for (const [key, value] of Object.entries(body)) {
6
+ if (allowed.has(key)) {
7
+ filtered[key] = value;
8
+ }
9
+ }
10
+ return filtered;
11
+ }
12
+ export function buildInsertQuery(table, fields, primaryKey) {
13
+ assertValidIdentifier(table, 'crud.table');
14
+ const columns = Object.keys(fields);
15
+ for (const col of columns) {
16
+ assertValidIdentifier(col, 'crud field');
17
+ }
18
+ const columnsList = columns.map(c => `[${c}]`).join(', ');
19
+ const paramsList = columns.map(c => `@${c}`).join(', ');
20
+ // Trigger-safe: INSERT + SELECT back by SCOPE_IDENTITY() instead of OUTPUT INSERTED.*
21
+ const selectBack = primaryKey
22
+ ? `; SELECT * FROM [${table}] WHERE [${primaryKey}] = SCOPE_IDENTITY()`
23
+ : '';
24
+ const sql = `INSERT INTO [${table}] (${columnsList}) VALUES (${paramsList})${selectBack}`;
25
+ return { sql, params: { ...fields } };
26
+ }
27
+ export function buildUpdateQuery(table, primaryKey, pkValue, fields) {
28
+ assertValidIdentifier(table, 'crud.table');
29
+ assertValidIdentifier(primaryKey, 'crud.primaryKey');
30
+ for (const col of Object.keys(fields)) {
31
+ assertValidIdentifier(col, 'crud field');
32
+ }
33
+ const setClauses = Object.keys(fields).map(c => `[${c}] = @${c}`).join(', ');
34
+ const sql = `UPDATE [${table}] SET ${setClauses} WHERE [${primaryKey}] = @_pkValue; SELECT * FROM [${table}] WHERE [${primaryKey}] = @_pkValue`;
35
+ return { sql, params: { ...fields, _pkValue: pkValue } };
36
+ }
37
+ export function buildDeleteQuery(table, primaryKey, pkValue) {
38
+ assertValidIdentifier(table, 'crud.table');
39
+ assertValidIdentifier(primaryKey, 'crud.primaryKey');
40
+ const sql = `DELETE FROM [${table}] WHERE [${primaryKey}] = @_pkValue`;
41
+ return { sql, params: { _pkValue: pkValue } };
42
+ }
43
+ //# sourceMappingURL=crud-builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crud-builder.js","sourceRoot":"","sources":["../src/crud-builder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { Handler } from './types.js';
2
+ export declare function discoverHandlers(handlersDir: string): Promise<Map<string, Handler>>;
3
+ export declare function validateHandlerRefs(refs: string[], handlers: Map<string, Handler>): string[];
4
+ export declare function getHandlerRefs(spec: {
5
+ endpoints?: Record<string, {
6
+ handler?: string;
7
+ }>;
8
+ }): string[];
9
+ //# sourceMappingURL=handler-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler-loader.d.ts","sourceRoot":"","sources":["../src/handler-loader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAK1C,wBAAsB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CA0BzF;AAED,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EAAE,EACd,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,MAAM,EAAE,CAQV;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,GAAG,MAAM,EAAE,CAKnG"}
@@ -0,0 +1,42 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { pathToFileURL } from 'url';
4
+ export async function discoverHandlers(handlersDir) {
5
+ const handlers = new Map();
6
+ if (!fs.existsSync(handlersDir)) {
7
+ return handlers;
8
+ }
9
+ const files = fs.readdirSync(handlersDir).filter(f => f.endsWith('.ts') || f.endsWith('.js') || f.endsWith('.mjs'));
10
+ for (const file of files) {
11
+ const name = path.basename(file, path.extname(file));
12
+ const fullPath = path.resolve(handlersDir, file);
13
+ try {
14
+ const mod = await import(pathToFileURL(fullPath).href);
15
+ const handler = mod.default;
16
+ if (typeof handler === 'function') {
17
+ handlers.set(name, handler);
18
+ }
19
+ }
20
+ catch {
21
+ // Skip files that fail to import
22
+ }
23
+ }
24
+ return handlers;
25
+ }
26
+ export function validateHandlerRefs(refs, handlers) {
27
+ const errors = [];
28
+ for (const ref of refs) {
29
+ if (!handlers.has(ref)) {
30
+ errors.push(`Handler "${ref}" referenced in spec but not found in handlers directory`);
31
+ }
32
+ }
33
+ return errors;
34
+ }
35
+ export function getHandlerRefs(spec) {
36
+ if (!spec.endpoints)
37
+ return [];
38
+ return Object.values(spec.endpoints)
39
+ .filter(e => e.handler)
40
+ .map(e => e.handler);
41
+ }
42
+ //# sourceMappingURL=handler-loader.js.map