@stratum-hq/sdk 1.0.1 → 1.2.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.
@@ -1,44 +1,48 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.expressMiddleware = expressMiddleware;
4
- const core_1 = require("@stratum-hq/core");
5
4
  const context_js_1 = require("../context.js");
6
5
  const jwt_js_1 = require("../resolvers/jwt.js");
7
- const header_js_1 = require("../resolvers/header.js");
6
+ const resolve_js_1 = require("../resolvers/resolve.js");
7
+ const tenant_errors_js_1 = require("./tenant-errors.js");
8
8
  function expressMiddleware(client, options) {
9
+ (0, jwt_js_1.assertJwtSupport)(options);
9
10
  return async (req, res, next) => {
11
+ /** Send the response for a known resolution error. Return false for any other error. */
12
+ const answerResolutionError = (err, tenantId) => {
13
+ const response = (0, tenant_errors_js_1.tenantErrorResponse)(err, tenantId);
14
+ if (response) {
15
+ res.status(response.status).json(response.body);
16
+ return true;
17
+ }
18
+ const failure = (0, tenant_errors_js_1.controlPlaneErrorResponse)(err);
19
+ if (!failure)
20
+ return false;
21
+ if (options?.onError && err instanceof Error) {
22
+ options.onError(err, req);
23
+ }
24
+ res.status(failure.status).json(failure.body);
25
+ return true;
26
+ };
10
27
  try {
11
28
  // Resolve tenant ID: JWT → header → custom resolvers
12
- let tenantId = null;
13
- tenantId = (0, jwt_js_1.resolveFromJwt)(req, options?.jwtClaimPath, {
14
- secret: options?.jwtSecret,
15
- verify: options?.jwtVerify,
16
- });
17
- if (!tenantId) {
18
- tenantId = (0, header_js_1.resolveFromHeader)(req);
19
- }
20
- if (!tenantId && options?.resolvers) {
21
- for (const resolver of options.resolvers) {
22
- const result = await resolver.resolve(req);
23
- if (result) {
24
- tenantId = result;
25
- break;
26
- }
27
- }
29
+ const resolution = await (0, resolve_js_1.resolveTenantId)(req, options);
30
+ if (resolution.status === "invalid_token") {
31
+ res.status(401).json({ error: { code: "INVALID_TOKEN", message: "Bearer token could not be verified" } });
32
+ return;
28
33
  }
29
- if (!tenantId) {
34
+ if (resolution.status === "missing") {
30
35
  res.status(400).json({ error: { code: "MISSING_TENANT", message: "Tenant ID could not be resolved from request" } });
31
36
  return;
32
37
  }
38
+ const tenantId = resolution.tenantId;
33
39
  let context;
34
40
  try {
35
41
  context = await client.resolveTenant(tenantId);
36
42
  }
37
43
  catch (err) {
38
- if (err instanceof core_1.TenantNotFoundError) {
39
- res.status(404).json({ error: { code: "TENANT_NOT_FOUND", message: `Tenant not found: ${tenantId}` } });
44
+ if (answerResolutionError(err, tenantId))
40
45
  return;
41
- }
42
46
  throw err;
43
47
  }
44
48
  req.tenant = context;
@@ -59,7 +63,15 @@ function expressMiddleware(client, options) {
59
63
  return;
60
64
  }
61
65
  // Resolve the impersonated tenant's context
62
- const impersonatedContext = await client.resolveTenant(impersonateTenantId);
66
+ let impersonatedContext;
67
+ try {
68
+ impersonatedContext = await client.resolveTenant(impersonateTenantId);
69
+ }
70
+ catch (err) {
71
+ if (answerResolutionError(err, impersonateTenantId))
72
+ return;
73
+ throw err;
74
+ }
63
75
  req.tenant = impersonatedContext;
64
76
  req.impersonating = true;
65
77
  req.originalTenantId = tenantId;
@@ -1 +1 @@
1
- {"version":3,"file":"express.js","sourceRoot":"","sources":["../../src/middleware/express.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"express.js","sourceRoot":"","sources":["../../src/middleware/express.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"fastify.d.ts","sourceRoot":"","sources":["../../src/middleware/fastify.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAOrD,KAAK,MAAM,GAAG,CAAC,GAAG,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;AAEtC,UAAU,kBAAkB;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;IACxD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAED,UAAU,gBAAgB;IACxB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG;QAAE,IAAI,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAA;KAAE,CAAC;CACrD;AAED,UAAU,WAAW;IACnB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IACpD,OAAO,CACL,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,CAAC,OAAO,EAAE,kBAAkB,EAAE,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,GACpF,IAAI,CAAC;CACT;AAED,wBAAgB,aAAa,CAC3B,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE;IAAE,MAAM,EAAE,aAAa,CAAA;CAAE,GAAG,iBAAiB,EACtD,IAAI,EAAE,MAAM,GACX,IAAI,CA0FN"}
1
+ {"version":3,"file":"fastify.d.ts","sourceRoot":"","sources":["../../src/middleware/fastify.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAQrD,KAAK,MAAM,GAAG,CAAC,GAAG,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;AAEtC,UAAU,kBAAkB;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;IACxD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAED,UAAU,gBAAgB;IACxB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG;QAAE,IAAI,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAA;KAAE,CAAC;CACrD;AAED,UAAU,WAAW;IACnB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IACpD,OAAO,CACL,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,CAAC,OAAO,EAAE,kBAAkB,EAAE,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,GACpF,IAAI,CAAC;CACT;AAED,wBAAgB,aAAa,CAC3B,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE;IAAE,MAAM,EAAE,aAAa,CAAA;CAAE,GAAG,iBAAiB,EACtD,IAAI,EAAE,MAAM,GACX,IAAI,CAkGN"}
@@ -1,48 +1,52 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.fastifyPlugin = fastifyPlugin;
4
- const core_1 = require("@stratum-hq/core");
5
4
  const context_js_1 = require("../context.js");
6
5
  const jwt_js_1 = require("../resolvers/jwt.js");
7
- const header_js_1 = require("../resolvers/header.js");
6
+ const resolve_js_1 = require("../resolvers/resolve.js");
7
+ const tenant_errors_js_1 = require("./tenant-errors.js");
8
8
  function fastifyPlugin(fastify, options, done) {
9
9
  const { client, ...middlewareOptions } = options;
10
+ (0, jwt_js_1.assertJwtSupport)(middlewareOptions);
10
11
  fastify.decorateRequest("tenant", null);
11
12
  fastify.decorateRequest("impersonating", false);
12
13
  fastify.decorateRequest("originalTenantId", null);
13
14
  fastify.addHook("onRequest", (request, reply, done) => {
14
- // Resolve tenant ID: JWT → header → custom resolvers
15
- let tenantId = null;
16
- tenantId = (0, jwt_js_1.resolveFromJwt)(request, middlewareOptions.jwtClaimPath, {
17
- secret: middlewareOptions.jwtSecret,
18
- verify: middlewareOptions.jwtVerify,
19
- });
20
- if (!tenantId) {
21
- tenantId = (0, header_js_1.resolveFromHeader)(request);
22
- }
15
+ /** Send the response for a known resolution error. Return false for any other error. */
16
+ const answerResolutionError = (err, tenantId) => {
17
+ const response = (0, tenant_errors_js_1.tenantErrorResponse)(err, tenantId);
18
+ if (response) {
19
+ reply.status(response.status).send(response.body);
20
+ return true;
21
+ }
22
+ const failure = (0, tenant_errors_js_1.controlPlaneErrorResponse)(err);
23
+ if (!failure)
24
+ return false;
25
+ if (middlewareOptions.onError && err instanceof Error) {
26
+ middlewareOptions.onError(err, request);
27
+ }
28
+ reply.status(failure.status).send(failure.body);
29
+ return true;
30
+ };
23
31
  const resolveAndRun = async () => {
24
- if (!tenantId && middlewareOptions.resolvers) {
25
- for (const resolver of middlewareOptions.resolvers) {
26
- const result = await resolver.resolve(request);
27
- if (result) {
28
- tenantId = result;
29
- break;
30
- }
31
- }
32
+ // Resolve tenant ID: JWT → header → custom resolvers
33
+ const resolution = await (0, resolve_js_1.resolveTenantId)(request, middlewareOptions);
34
+ if (resolution.status === "invalid_token") {
35
+ reply.status(401).send({ error: { code: "INVALID_TOKEN", message: "Bearer token could not be verified" } });
36
+ return;
32
37
  }
33
- if (!tenantId) {
38
+ if (resolution.status === "missing") {
34
39
  reply.status(400).send({ error: { code: "MISSING_TENANT", message: "Tenant ID could not be resolved from request" } });
35
40
  return;
36
41
  }
42
+ const tenantId = resolution.tenantId;
37
43
  let context;
38
44
  try {
39
45
  context = await client.resolveTenant(tenantId);
40
46
  }
41
47
  catch (err) {
42
- if (err instanceof core_1.TenantNotFoundError) {
43
- reply.status(404).send({ error: { code: "TENANT_NOT_FOUND", message: `Tenant not found: ${tenantId}` } });
48
+ if (answerResolutionError(err, tenantId))
44
49
  return;
45
- }
46
50
  if (middlewareOptions.onError && err instanceof Error) {
47
51
  middlewareOptions.onError(err, request);
48
52
  }
@@ -65,7 +69,15 @@ function fastifyPlugin(fastify, options, done) {
65
69
  });
66
70
  return;
67
71
  }
68
- const impersonatedContext = await client.resolveTenant(impersonateTenantId);
72
+ let impersonatedContext;
73
+ try {
74
+ impersonatedContext = await client.resolveTenant(impersonateTenantId);
75
+ }
76
+ catch (err) {
77
+ if (answerResolutionError(err, impersonateTenantId))
78
+ return;
79
+ throw err;
80
+ }
69
81
  request.tenant = impersonatedContext;
70
82
  request.impersonating = true;
71
83
  request.originalTenantId = tenantId;
@@ -81,4 +93,8 @@ function fastifyPlugin(fastify, options, done) {
81
93
  });
82
94
  done();
83
95
  }
96
+ // Register the hook and decorators on the instance that calls
97
+ // `register(fastifyPlugin)`, not in an encapsulated child context, so routes
98
+ // declared on that instance run the tenant hook (the fastify-plugin convention).
99
+ fastifyPlugin[Symbol.for("skip-override")] = true;
84
100
  //# sourceMappingURL=fastify.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"fastify.js","sourceRoot":"","sources":["../../src/middleware/fastify.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fastify.js","sourceRoot":"","sources":["../../src/middleware/fastify.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ export interface TenantErrorResponse {
2
+ status: number;
3
+ body: {
4
+ error: {
5
+ code: string;
6
+ message: string;
7
+ };
8
+ };
9
+ }
10
+ /**
11
+ * Return the HTTP response for a tenant resolution error, or null when the
12
+ * error is not a known tenant error. The adapters send this response instead
13
+ * of passing the error on, where it would become a 500. A tenant error is a
14
+ * normal answer for the request, so the adapters do not call `onError` for it.
15
+ */
16
+ export declare function tenantErrorResponse(err: unknown, tenantId: string): TenantErrorResponse | null;
17
+ /**
18
+ * Return the HTTP response for a control plane failure during tenant
19
+ * resolution, or null for any other error.
20
+ *
21
+ * A timeout answers 504. A rejected SDK API key answers 500, because the
22
+ * fault is in the server configuration and not in the request. For a rejected
23
+ * key, this function also writes the cause to `console.error`, because the
24
+ * response body does not show it.
25
+ */
26
+ export declare function controlPlaneErrorResponse(err: unknown): TenantErrorResponse | null;
27
+ //# sourceMappingURL=tenant-errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenant-errors.d.ts","sourceRoot":"","sources":["../../src/middleware/tenant-errors.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE;QAAE,KAAK,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;CACpD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,mBAAmB,GAAG,IAAI,CAc9F;AAED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,OAAO,GAAG,mBAAmB,GAAG,IAAI,CAiBlF"}
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.tenantErrorResponse = tenantErrorResponse;
4
+ exports.controlPlaneErrorResponse = controlPlaneErrorResponse;
5
+ const core_1 = require("@stratum-hq/core");
6
+ /**
7
+ * Return the HTTP response for a tenant resolution error, or null when the
8
+ * error is not a known tenant error. The adapters send this response instead
9
+ * of passing the error on, where it would become a 500. A tenant error is a
10
+ * normal answer for the request, so the adapters do not call `onError` for it.
11
+ */
12
+ function tenantErrorResponse(err, tenantId) {
13
+ if (err instanceof core_1.TenantNotFoundError) {
14
+ return { status: 404, body: { error: { code: "TENANT_NOT_FOUND", message: `Tenant not found: ${tenantId}` } } };
15
+ }
16
+ if (err instanceof core_1.TenantSuspendedError) {
17
+ return { status: 403, body: { error: { code: "TENANT_SUSPENDED", message: `Tenant ${tenantId} is suspended` } } };
18
+ }
19
+ if (err instanceof core_1.TenantArchivedError) {
20
+ return { status: 410, body: { error: { code: "TENANT_ARCHIVED", message: `Tenant ${tenantId} is archived` } } };
21
+ }
22
+ if (err instanceof core_1.ForbiddenError) {
23
+ return { status: 403, body: { error: { code: "FORBIDDEN", message: `Access to tenant ${tenantId} is denied` } } };
24
+ }
25
+ return null;
26
+ }
27
+ /**
28
+ * Return the HTTP response for a control plane failure during tenant
29
+ * resolution, or null for any other error.
30
+ *
31
+ * A timeout answers 504. A rejected SDK API key answers 500, because the
32
+ * fault is in the server configuration and not in the request. For a rejected
33
+ * key, this function also writes the cause to `console.error`, because the
34
+ * response body does not show it.
35
+ */
36
+ function controlPlaneErrorResponse(err) {
37
+ if (err instanceof Error && err.name === "TimeoutError") {
38
+ return {
39
+ status: 504,
40
+ body: { error: { code: "CONTROL_PLANE_TIMEOUT", message: "The Stratum control plane did not respond in time" } },
41
+ };
42
+ }
43
+ if (err instanceof core_1.UnauthorizedError) {
44
+ console.error("[stratum] The control plane rejected the SDK API key (401). Check the apiKey option of StratumClient.");
45
+ return {
46
+ status: 500,
47
+ body: { error: { code: "CONTROL_PLANE_AUTH_FAILED", message: "Tenant resolution failed" } },
48
+ };
49
+ }
50
+ return null;
51
+ }
52
+ //# sourceMappingURL=tenant-errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenant-errors.js","sourceRoot":"","sources":["../../src/middleware/tenant-errors.ts"],"names":[],"mappings":";;AAmBA,kDAcC;AAWD,8DAiBC;AA7DD,2CAM0B;AAO1B;;;;;GAKG;AACH,SAAgB,mBAAmB,CAAC,GAAY,EAAE,QAAgB;IAChE,IAAI,GAAG,YAAY,0BAAmB,EAAE,CAAC;QACvC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,qBAAqB,QAAQ,EAAE,EAAE,EAAE,EAAE,CAAC;IAClH,CAAC;IACD,IAAI,GAAG,YAAY,2BAAoB,EAAE,CAAC;QACxC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,UAAU,QAAQ,eAAe,EAAE,EAAE,EAAE,CAAC;IACpH,CAAC;IACD,IAAI,GAAG,YAAY,0BAAmB,EAAE,CAAC;QACvC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,UAAU,QAAQ,cAAc,EAAE,EAAE,EAAE,CAAC;IAClH,CAAC;IACD,IAAI,GAAG,YAAY,qBAAc,EAAE,CAAC;QAClC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,oBAAoB,QAAQ,YAAY,EAAE,EAAE,EAAE,CAAC;IACpH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,yBAAyB,CAAC,GAAY;IACpD,IAAI,GAAG,YAAY,KAAK,IAAI,GAAG,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;QACxD,OAAO;YACL,MAAM,EAAE,GAAG;YACX,IAAI,EAAE,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,uBAAuB,EAAE,OAAO,EAAE,mDAAmD,EAAE,EAAE;SACjH,CAAC;IACJ,CAAC;IACD,IAAI,GAAG,YAAY,wBAAiB,EAAE,CAAC;QACrC,OAAO,CAAC,KAAK,CACX,uGAAuG,CACxG,CAAC;QACF,OAAO;YACL,MAAM,EAAE,GAAG;YACX,IAAI,EAAE,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,2BAA2B,EAAE,OAAO,EAAE,0BAA0B,EAAE,EAAE;SAC5F,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -1,2 +1,6 @@
1
- export declare function resolveFromHeader(req: unknown): string | null;
1
+ /**
2
+ * Read the tenant ID from a request header. When `headerName` is given, only
3
+ * that header is read; otherwise the default X-Tenant-ID header is used.
4
+ */
5
+ export declare function resolveFromHeader(req: unknown, headerName?: string): string | null;
2
6
  //# sourceMappingURL=header.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"header.d.ts","sourceRoot":"","sources":["../../src/resolvers/header.ts"],"names":[],"mappings":"AAEA,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAO7D"}
1
+ {"version":3,"file":"header.d.ts","sourceRoot":"","sources":["../../src/resolvers/header.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CASlF"}
@@ -2,12 +2,18 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.resolveFromHeader = resolveFromHeader;
4
4
  const core_1 = require("@stratum-hq/core");
5
- function resolveFromHeader(req) {
5
+ /**
6
+ * Read the tenant ID from a request header. When `headerName` is given, only
7
+ * that header is read; otherwise the default X-Tenant-ID header is used.
8
+ */
9
+ function resolveFromHeader(req, headerName) {
6
10
  const r = req;
7
11
  const headers = r["headers"];
8
12
  if (!headers)
9
13
  return null;
10
- const value = headers[core_1.TENANT_HEADER.toLowerCase()] ?? headers[core_1.TENANT_HEADER];
14
+ const value = headerName
15
+ ? headers[headerName.toLowerCase()]
16
+ : headers[core_1.TENANT_HEADER.toLowerCase()] ?? headers[core_1.TENANT_HEADER];
11
17
  if (!value)
12
18
  return null;
13
19
  return Array.isArray(value) ? value[0] ?? null : value;
@@ -1 +1 @@
1
- {"version":3,"file":"header.js","sourceRoot":"","sources":["../../src/resolvers/header.ts"],"names":[],"mappings":";;AAEA,8CAOC;AATD,2CAAiD;AAEjD,SAAgB,iBAAiB,CAAC,GAAY;IAC5C,MAAM,CAAC,GAAG,GAA8B,CAAC;IACzC,MAAM,OAAO,GAAG,CAAC,CAAC,SAAS,CAA8D,CAAC;IAC1F,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC1B,MAAM,KAAK,GAAG,OAAO,CAAC,oBAAa,CAAC,WAAW,EAAE,CAAC,IAAI,OAAO,CAAC,oBAAa,CAAC,CAAC;IAC7E,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;AACzD,CAAC"}
1
+ {"version":3,"file":"header.js","sourceRoot":"","sources":["../../src/resolvers/header.ts"],"names":[],"mappings":";;AAMA,8CASC;AAfD,2CAAiD;AAEjD;;;GAGG;AACH,SAAgB,iBAAiB,CAAC,GAAY,EAAE,UAAmB;IACjE,MAAM,CAAC,GAAG,GAA8B,CAAC;IACzC,MAAM,OAAO,GAAG,CAAC,CAAC,SAAS,CAA8D,CAAC;IAC1F,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC1B,MAAM,KAAK,GAAG,UAAU;QACtB,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC;QACnC,CAAC,CAAC,OAAO,CAAC,oBAAa,CAAC,WAAW,EAAE,CAAC,IAAI,OAAO,CAAC,oBAAa,CAAC,CAAC;IACnE,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;AACzD,CAAC"}
@@ -2,5 +2,33 @@ export interface JwtResolverOptions {
2
2
  secret?: string;
3
3
  verify?: (token: string) => Record<string, unknown> | null;
4
4
  }
5
+ /**
6
+ * Throw if `jwtSecret` is configured without a `jwtVerify` function and the
7
+ * optional `jsonwebtoken` peer dependency cannot be loaded. Called when a
8
+ * middleware is constructed, so a missing module fails loudly at startup
9
+ * instead of making every token look unverifiable.
10
+ */
11
+ export declare function assertJwtSupport(options?: {
12
+ jwtSecret?: string;
13
+ jwtVerify?: unknown;
14
+ }): void;
15
+ /**
16
+ * Outcome of JWT tenant resolution:
17
+ * - `absent`: no bearer token, or no secret / verify function configured
18
+ * - `invalid`: a bearer token was sent but did not verify
19
+ * - `no_claim`: the token verified but carries no string tenant claim
20
+ * - `resolved`: the token verified and carries the tenant claim
21
+ */
22
+ export type JwtTenantResolution = {
23
+ status: "absent";
24
+ } | {
25
+ status: "invalid";
26
+ } | {
27
+ status: "no_claim";
28
+ } | {
29
+ status: "resolved";
30
+ tenantId: string;
31
+ };
32
+ export declare function resolveJwtTenant(req: unknown, claimPath?: string, options?: JwtResolverOptions): JwtTenantResolution;
5
33
  export declare function resolveFromJwt(req: unknown, claimPath?: string, options?: JwtResolverOptions): string | null;
6
34
  //# sourceMappingURL=jwt.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../../src/resolvers/jwt.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC5D;AAcD,wBAAgB,cAAc,CAC5B,GAAG,EAAE,OAAO,EACZ,SAAS,GAAE,MAAoB,EAC/B,OAAO,CAAC,EAAE,kBAAkB,GAC3B,MAAM,GAAG,IAAI,CAuCf"}
1
+ {"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../../src/resolvers/jwt.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC5D;AAkBD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAQ5F;AAgBD;;;;;;GAMG;AACH,MAAM,MAAM,mBAAmB,GAC3B;IAAE,MAAM,EAAE,QAAQ,CAAA;CAAE,GACpB;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,GACrB;IAAE,MAAM,EAAE,UAAU,CAAA;CAAE,GACtB;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7C,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,OAAO,EACZ,SAAS,GAAE,MAAoB,EAC/B,OAAO,CAAC,EAAE,kBAAkB,GAC3B,mBAAmB,CAuCrB;AAED,wBAAgB,cAAc,CAC5B,GAAG,EAAE,OAAO,EACZ,SAAS,GAAE,MAAoB,EAC/B,OAAO,CAAC,EAAE,kBAAkB,GAC3B,MAAM,GAAG,IAAI,CAGf"}
@@ -1,10 +1,43 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.assertJwtSupport = assertJwtSupport;
4
+ exports.resolveJwtTenant = resolveJwtTenant;
3
5
  exports.resolveFromJwt = resolveFromJwt;
4
- function verifyWithJsonwebtoken(token, secret) {
6
+ /**
7
+ * Load the optional `jsonwebtoken` peer dependency. Returns null only when the
8
+ * module is not installed; any other load failure is rethrown.
9
+ */
10
+ function loadJsonwebtoken() {
5
11
  try {
6
12
  // eslint-disable-next-line @typescript-eslint/no-require-imports
7
- const jwt = require("jsonwebtoken");
13
+ return require("jsonwebtoken");
14
+ }
15
+ catch (err) {
16
+ if (err.code === "MODULE_NOT_FOUND")
17
+ return null;
18
+ throw err;
19
+ }
20
+ }
21
+ /**
22
+ * Throw if `jwtSecret` is configured without a `jwtVerify` function and the
23
+ * optional `jsonwebtoken` peer dependency cannot be loaded. Called when a
24
+ * middleware is constructed, so a missing module fails loudly at startup
25
+ * instead of making every token look unverifiable.
26
+ */
27
+ function assertJwtSupport(options) {
28
+ if (!options?.jwtSecret || options.jwtVerify)
29
+ return;
30
+ if (!loadJsonwebtoken()) {
31
+ throw new Error("[stratum] jwtSecret is set but the optional peer dependency 'jsonwebtoken' could not be loaded. " +
32
+ "Install jsonwebtoken, or provide jwtVerify.");
33
+ }
34
+ }
35
+ function verifyWithJsonwebtoken(token, secret) {
36
+ const jwt = loadJsonwebtoken();
37
+ if (!jwt) {
38
+ throw new Error("[stratum] jwtSecret is set but the optional peer dependency 'jsonwebtoken' is not installed.");
39
+ }
40
+ try {
8
41
  const decoded = jwt.verify(token, secret, { algorithms: ["HS256"] });
9
42
  if (typeof decoded === "string")
10
43
  return null;
@@ -14,15 +47,15 @@ function verifyWithJsonwebtoken(token, secret) {
14
47
  return null;
15
48
  }
16
49
  }
17
- function resolveFromJwt(req, claimPath = "tenant_id", options) {
50
+ function resolveJwtTenant(req, claimPath = "tenant_id", options) {
18
51
  const r = req;
19
52
  const headers = r["headers"];
20
53
  if (!headers)
21
- return null;
54
+ return { status: "absent" };
22
55
  const authHeader = headers["authorization"];
23
56
  const auth = Array.isArray(authHeader) ? authHeader[0] : authHeader;
24
57
  if (!auth || !auth.startsWith("Bearer "))
25
- return null;
58
+ return { status: "absent" };
26
59
  const token = auth.slice(7);
27
60
  let claims = null;
28
61
  if (options?.verify) {
@@ -37,18 +70,22 @@ function resolveFromJwt(req, claimPath = "tenant_id", options) {
37
70
  // No secret or verify function provided — refuse to trust unsigned tokens
38
71
  console.warn("[stratum] JWT ignored: no jwtSecret or jwtVerify provided. " +
39
72
  "Configure middleware options to enable JWT tenant resolution.");
40
- return null;
73
+ return { status: "absent" };
41
74
  }
42
75
  if (!claims)
43
- return null;
76
+ return { status: "invalid" };
44
77
  // Support dotted claim paths like "stratum.tenant_id"
45
78
  const segments = claimPath.split(".");
46
79
  let current = claims;
47
80
  for (const seg of segments) {
48
81
  if (current == null || typeof current !== "object")
49
- return null;
82
+ return { status: "no_claim" };
50
83
  current = current[seg];
51
84
  }
52
- return typeof current === "string" ? current : null;
85
+ return typeof current === "string" ? { status: "resolved", tenantId: current } : { status: "no_claim" };
86
+ }
87
+ function resolveFromJwt(req, claimPath = "tenant_id", options) {
88
+ const result = resolveJwtTenant(req, claimPath, options);
89
+ return result.status === "resolved" ? result.tenantId : null;
53
90
  }
54
91
  //# sourceMappingURL=jwt.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"jwt.js","sourceRoot":"","sources":["../../src/resolvers/jwt.ts"],"names":[],"mappings":";;AAiBA,wCA2CC;AAvDD,SAAS,sBAAsB,CAAC,KAAa,EAAE,MAAc;IAC3D,IAAI,CAAC;QACH,iEAAiE;QACjE,MAAM,GAAG,GAAG,OAAO,CAAC,cAAc,CAAkC,CAAC;QACrE,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACrE,IAAI,OAAO,OAAO,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QAC7C,OAAO,OAAkC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAgB,cAAc,CAC5B,GAAY,EACZ,YAAoB,WAAW,EAC/B,OAA4B;IAE5B,MAAM,CAAC,GAAG,GAA8B,CAAC;IACzC,MAAM,OAAO,GAAG,CAAC,CAAC,SAAS,CAA8D,CAAC;IAC1F,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAE1B,MAAM,UAAU,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IAC5C,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;IACpE,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IAEtD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAE5B,IAAI,MAAM,GAAmC,IAAI,CAAC;IAElD,IAAI,OAAO,EAAE,MAAM,EAAE,CAAC;QACpB,+CAA+C;QAC/C,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACjC,CAAC;SAAM,IAAI,OAAO,EAAE,MAAM,EAAE,CAAC;QAC3B,0CAA0C;QAC1C,MAAM,GAAG,sBAAsB,CAAC,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IACzD,CAAC;SAAM,CAAC;QACN,0EAA0E;QAC1E,OAAO,CAAC,IAAI,CACV,6DAA6D;YAC7D,+DAA+D,CAChE,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAEzB,sDAAsD;IACtD,MAAM,QAAQ,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACtC,IAAI,OAAO,GAAY,MAAM,CAAC;IAC9B,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,IAAI,OAAO,IAAI,IAAI,IAAI,OAAO,OAAO,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QAChE,OAAO,GAAI,OAAmC,CAAC,GAAG,CAAC,CAAC;IACtD,CAAC;IAED,OAAO,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;AACtD,CAAC"}
1
+ {"version":3,"file":"jwt.js","sourceRoot":"","sources":["../../src/resolvers/jwt.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { MiddlewareOptions } from "../types.js";
2
+ /**
3
+ * Outcome of request tenant resolution. `invalid_token` means a bearer token
4
+ * was sent while JWT verification is configured and it did not verify; the
5
+ * caller must reject the request rather than try other sources.
6
+ */
7
+ export type TenantIdResolution = {
8
+ status: "resolved";
9
+ tenantId: string;
10
+ } | {
11
+ status: "missing";
12
+ } | {
13
+ status: "invalid_token";
14
+ };
15
+ /**
16
+ * Resolve the tenant ID for a request: verified JWT, then the tenant header,
17
+ * then custom resolvers.
18
+ *
19
+ * When `jwtSecret` or `jwtVerify` is configured, the JWT is the tenant binding:
20
+ * a token that fails verification is reported as `invalid_token`, and the
21
+ * client-supplied tenant header is not consulted unless `trustTenantHeader`
22
+ * is true.
23
+ */
24
+ export declare function resolveTenantId(req: unknown, options?: MiddlewareOptions): Promise<TenantIdResolution>;
25
+ //# sourceMappingURL=resolve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/resolvers/resolve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAIrD;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAC1B;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACxC;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,GACrB;IAAE,MAAM,EAAE,eAAe,CAAA;CAAE,CAAC;AAEhC;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAuB5G"}
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveTenantId = resolveTenantId;
4
+ const jwt_js_1 = require("./jwt.js");
5
+ const header_js_1 = require("./header.js");
6
+ /**
7
+ * Resolve the tenant ID for a request: verified JWT, then the tenant header,
8
+ * then custom resolvers.
9
+ *
10
+ * When `jwtSecret` or `jwtVerify` is configured, the JWT is the tenant binding:
11
+ * a token that fails verification is reported as `invalid_token`, and the
12
+ * client-supplied tenant header is not consulted unless `trustTenantHeader`
13
+ * is true.
14
+ */
15
+ async function resolveTenantId(req, options) {
16
+ const jwtConfigured = Boolean(options?.jwtSecret || options?.jwtVerify);
17
+ const jwt = (0, jwt_js_1.resolveJwtTenant)(req, options?.jwtClaimPath, {
18
+ secret: options?.jwtSecret,
19
+ verify: options?.jwtVerify,
20
+ });
21
+ if (jwt.status === "resolved")
22
+ return { status: "resolved", tenantId: jwt.tenantId };
23
+ if (jwt.status === "invalid")
24
+ return { status: "invalid_token" };
25
+ if (!jwtConfigured || options?.trustTenantHeader) {
26
+ const fromHeader = (0, header_js_1.resolveFromHeader)(req, options?.headerName);
27
+ if (fromHeader)
28
+ return { status: "resolved", tenantId: fromHeader };
29
+ }
30
+ if (options?.resolvers) {
31
+ for (const resolver of options.resolvers) {
32
+ const result = await resolver.resolve(req);
33
+ if (result)
34
+ return { status: "resolved", tenantId: result };
35
+ }
36
+ }
37
+ return { status: "missing" };
38
+ }
39
+ //# sourceMappingURL=resolve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/resolvers/resolve.ts"],"names":[],"mappings":";;AAuBA,0CAuBC;AA7CD,qCAA4C;AAC5C,2CAAgD;AAYhD;;;;;;;;GAQG;AACI,KAAK,UAAU,eAAe,CAAC,GAAY,EAAE,OAA2B;IAC7E,MAAM,aAAa,GAAG,OAAO,CAAC,OAAO,EAAE,SAAS,IAAI,OAAO,EAAE,SAAS,CAAC,CAAC;IAExE,MAAM,GAAG,GAAG,IAAA,yBAAgB,EAAC,GAAG,EAAE,OAAO,EAAE,YAAY,EAAE;QACvD,MAAM,EAAE,OAAO,EAAE,SAAS;QAC1B,MAAM,EAAE,OAAO,EAAE,SAAS;KAC3B,CAAC,CAAC;IACH,IAAI,GAAG,CAAC,MAAM,KAAK,UAAU;QAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,CAAC;IACrF,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC;IAEjE,IAAI,CAAC,aAAa,IAAI,OAAO,EAAE,iBAAiB,EAAE,CAAC;QACjD,MAAM,UAAU,GAAG,IAAA,6BAAiB,EAAC,GAAG,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;QAC/D,IAAI,UAAU;YAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;IACtE,CAAC;IAED,IAAI,OAAO,EAAE,SAAS,EAAE,CAAC;QACvB,KAAK,MAAM,QAAQ,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YACzC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAC3C,IAAI,MAAM;gBAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;QAC9D,CAAC;IACH,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;AAC/B,CAAC"}
package/dist/types.d.ts CHANGED
@@ -3,10 +3,19 @@ export type { ResolvedTenantContext, TenantNode, CreateTenantInput, UpdateTenant
3
3
  export type { TenantResolver } from "./resolvers/custom.js";
4
4
  export interface MiddlewareOptions {
5
5
  resolvers?: import("./resolvers/custom.js").TenantResolver[];
6
+ /** Header to read the tenant ID from. When set, only this header is read (default: X-Tenant-ID). */
6
7
  headerName?: string;
7
8
  jwtClaimPath?: string;
9
+ /** HS256 secret for JWT verification. Requires the optional `jsonwebtoken` peer dependency unless `jwtVerify` is given. */
8
10
  jwtSecret?: string;
9
11
  jwtVerify?: (token: string) => Record<string, unknown> | null;
12
+ /**
13
+ * When `jwtSecret` or `jwtVerify` is configured, the tenant header is ignored
14
+ * unless this is true, so the verified JWT is the only tenant binding. A
15
+ * bearer token that fails verification is always rejected with 401. Has no
16
+ * effect when JWT verification is not configured. Default: false.
17
+ */
18
+ trustTenantHeader?: boolean;
10
19
  onError?: (err: Error, req: unknown) => void;
11
20
  /**
12
21
  * Enable tenant impersonation via X-Impersonate-Tenant header.
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,UAAU,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAErJ,YAAY,EAAE,qBAAqB,EAAE,UAAU,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,eAAe,EAAE,kBAAkB,EAAE,CAAC;AAE7H,YAAY,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,EAAE,OAAO,uBAAuB,EAAE,cAAc,EAAE,CAAC;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC9D,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;IAC7C;;;;;;;;OAQG;IACH,aAAa,CAAC,EAAE;QACd,OAAO,EAAE,OAAO,CAAC;QACjB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QACxG,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,KAAK,IAAI,CAAC;KACxF,CAAC;CACH"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,UAAU,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAErJ,YAAY,EAAE,qBAAqB,EAAE,UAAU,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,eAAe,EAAE,kBAAkB,EAAE,CAAC;AAE7H,YAAY,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,EAAE,OAAO,uBAAuB,EAAE,cAAc,EAAE,CAAC;IAC7D,oGAAoG;IACpG,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2HAA2H;IAC3H,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC9D;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;IAC7C;;;;;;;;OAQG;IACH,aAAa,CAAC,EAAE;QACd,OAAO,EAAE,OAAO,CAAC;QACjB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QACxG,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,KAAK,IAAI,CAAC;KACxF,CAAC;CACH"}