@heavybit/pendoadmin-shared-lib 1.0.0 → 1.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 (61) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +4 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/rate-limit/create-rate-limiter.d.ts +23 -0
  6. package/dist/rate-limit/create-rate-limiter.d.ts.map +1 -0
  7. package/dist/rate-limit/create-rate-limiter.js +60 -0
  8. package/dist/rate-limit/create-rate-limiter.js.map +1 -0
  9. package/dist/rate-limit/index.d.ts +3 -0
  10. package/dist/rate-limit/index.d.ts.map +1 -0
  11. package/dist/rate-limit/index.js +2 -0
  12. package/dist/rate-limit/index.js.map +1 -0
  13. package/dist/scheduling/event-driven-scheduler.d.ts +33 -0
  14. package/dist/scheduling/event-driven-scheduler.d.ts.map +1 -0
  15. package/dist/scheduling/event-driven-scheduler.js +67 -0
  16. package/dist/scheduling/event-driven-scheduler.js.map +1 -0
  17. package/dist/scheduling/index.d.ts +3 -0
  18. package/dist/scheduling/index.d.ts.map +1 -0
  19. package/dist/scheduling/index.js +2 -0
  20. package/dist/scheduling/index.js.map +1 -0
  21. package/dist/socket/create-socket-server.d.ts +30 -0
  22. package/dist/socket/create-socket-server.d.ts.map +1 -0
  23. package/dist/socket/create-socket-server.js +59 -0
  24. package/dist/socket/create-socket-server.js.map +1 -0
  25. package/dist/socket/index.d.ts +5 -0
  26. package/dist/socket/index.d.ts.map +1 -0
  27. package/dist/socket/index.js +3 -0
  28. package/dist/socket/index.js.map +1 -0
  29. package/dist/socket/nestjs-socket.module.d.ts +20 -0
  30. package/dist/socket/nestjs-socket.module.d.ts.map +1 -0
  31. package/dist/socket/nestjs-socket.module.js +64 -0
  32. package/dist/socket/nestjs-socket.module.js.map +1 -0
  33. package/dist/tenant/drizzle-tenant.d.ts +25 -0
  34. package/dist/tenant/drizzle-tenant.d.ts.map +1 -0
  35. package/dist/tenant/drizzle-tenant.js +35 -0
  36. package/dist/tenant/drizzle-tenant.js.map +1 -0
  37. package/dist/tenant/index.d.ts +7 -0
  38. package/dist/tenant/index.d.ts.map +1 -0
  39. package/dist/tenant/index.js +7 -0
  40. package/dist/tenant/index.js.map +1 -0
  41. package/dist/tenant/knex-tenant.d.ts +13 -0
  42. package/dist/tenant/knex-tenant.d.ts.map +1 -0
  43. package/dist/tenant/knex-tenant.js +43 -0
  44. package/dist/tenant/knex-tenant.js.map +1 -0
  45. package/dist/tenant/tenant-access-control.d.ts +93 -0
  46. package/dist/tenant/tenant-access-control.d.ts.map +1 -0
  47. package/dist/tenant/tenant-access-control.js +260 -0
  48. package/dist/tenant/tenant-access-control.js.map +1 -0
  49. package/package.json +33 -4
  50. package/src/index.ts +6 -0
  51. package/src/rate-limit/create-rate-limiter.ts +107 -0
  52. package/src/rate-limit/index.ts +2 -0
  53. package/src/scheduling/event-driven-scheduler.ts +87 -0
  54. package/src/scheduling/index.ts +2 -0
  55. package/src/socket/create-socket-server.ts +112 -0
  56. package/src/socket/index.ts +14 -0
  57. package/src/socket/nestjs-socket.module.ts +77 -0
  58. package/src/tenant/drizzle-tenant.ts +51 -0
  59. package/src/tenant/index.ts +24 -0
  60. package/src/tenant/knex-tenant.ts +59 -0
  61. package/src/tenant/tenant-access-control.ts +370 -0
package/dist/index.d.ts CHANGED
@@ -6,4 +6,6 @@ export * from './config/index.js';
6
6
  export * from './messaging/index.js';
7
7
  export * from './database/index.js';
8
8
  export * from './express/index.js';
9
+ export * from './rate-limit/index.js';
10
+ export * from './scheduling/index.js';
9
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,kBAAkB,CAAC;AAGjC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,uBAAuB,CAAC;AAGtC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,sBAAsB,CAAC;AAGrC,cAAc,qBAAqB,CAAC;AAGpC,cAAc,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,kBAAkB,CAAC;AAGjC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,uBAAuB,CAAC;AAGtC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,sBAAsB,CAAC;AAGrC,cAAc,qBAAqB,CAAC;AAGpC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,uBAAuB,CAAC;AAGtC,cAAc,uBAAuB,CAAC"}
package/dist/index.js CHANGED
@@ -14,4 +14,8 @@ export * from './messaging/index.js';
14
14
  export * from './database/index.js';
15
15
  // Express middleware (for Express-based services)
16
16
  export * from './express/index.js';
17
+ // Rate limiting
18
+ export * from './rate-limit/index.js';
19
+ // Scheduling
20
+ export * from './scheduling/index.js';
17
21
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,QAAQ;AACR,cAAc,kBAAkB,CAAC;AAEjC,mBAAmB;AACnB,cAAc,mBAAmB,CAAC;AAElC,aAAa;AACb,cAAc,uBAAuB,CAAC;AAEtC,UAAU;AACV,cAAc,oBAAoB,CAAC;AAEnC,SAAS;AACT,cAAc,mBAAmB,CAAC;AAElC,YAAY;AACZ,cAAc,sBAAsB,CAAC;AAErC,WAAW;AACX,cAAc,qBAAqB,CAAC;AAEpC,kDAAkD;AAClD,cAAc,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,QAAQ;AACR,cAAc,kBAAkB,CAAC;AAEjC,mBAAmB;AACnB,cAAc,mBAAmB,CAAC;AAElC,aAAa;AACb,cAAc,uBAAuB,CAAC;AAEtC,UAAU;AACV,cAAc,oBAAoB,CAAC;AAEnC,SAAS;AACT,cAAc,mBAAmB,CAAC;AAElC,YAAY;AACZ,cAAc,sBAAsB,CAAC;AAErC,WAAW;AACX,cAAc,qBAAqB,CAAC;AAEpC,kDAAkD;AAClD,cAAc,oBAAoB,CAAC;AAEnC,gBAAgB;AAChB,cAAc,uBAAuB,CAAC;AAEtC,aAAa;AACb,cAAc,uBAAuB,CAAC"}
@@ -0,0 +1,23 @@
1
+ import { Request, Response, NextFunction } from "express";
2
+ import { RateLimiterAbstract } from "rate-limiter-flexible";
3
+ import type Redis from "ioredis";
4
+ export interface RateLimitConfig {
5
+ /** Max requests per window (default: 100) */
6
+ maxRequests?: number;
7
+ /** Window duration in milliseconds (default: 60 000) */
8
+ windowMs?: number;
9
+ /** Redis key prefix — use unique per service to avoid collisions (default: "rl:service") */
10
+ keyPrefix?: string;
11
+ /** Optional Redis client — falls back to in-memory if omitted */
12
+ redisClient?: Redis | null;
13
+ /** Extract the rate-limit key from the request. Default: X-User-Id header → IP → "unknown" */
14
+ keyExtractor?: (req: Request) => string;
15
+ }
16
+ export interface RateLimitResult {
17
+ /** The rate-limiter instance (for advanced use) */
18
+ limiter: RateLimiterAbstract;
19
+ /** Express middleware */
20
+ middleware: (req: Request, res: Response, next: NextFunction) => Promise<void>;
21
+ }
22
+ export declare function createRateLimiter(cfg?: RateLimitConfig): RateLimitResult;
23
+ //# sourceMappingURL=create-rate-limiter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-rate-limiter.d.ts","sourceRoot":"","sources":["../../src/rate-limit/create-rate-limiter.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAuC,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACjG,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC;AAIjC,MAAM,WAAW,eAAe;IAC9B,6CAA6C;IAC7C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4FAA4F;IAC5F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iEAAiE;IACjE,WAAW,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC;IAC3B,8FAA8F;IAC9F,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,MAAM,CAAC;CACzC;AAED,MAAM,WAAW,eAAe;IAC9B,mDAAmD;IACnD,OAAO,EAAE,mBAAmB,CAAC;IAC7B,yBAAyB;IACzB,UAAU,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAChF;AAID,wBAAgB,iBAAiB,CAAC,GAAG,GAAE,eAAoB,GAAG,eAAe,CAmE5E"}
@@ -0,0 +1,60 @@
1
+ // pendoadmin-shared-lib/src/rate-limit/create-rate-limiter.ts
2
+ //
3
+ // Factory that creates a dual-layer (Redis + memory fallback) rate limiter.
4
+ // Available for any Express-based service.
5
+ import { RateLimiterRedis, RateLimiterMemory } from "rate-limiter-flexible";
6
+ // ─── Factory ────────────────────────────────────────────
7
+ export function createRateLimiter(cfg = {}) {
8
+ const { maxRequests = 100, windowMs = 60_000, keyPrefix = "rl:service", redisClient = null, keyExtractor = defaultKeyExtractor, } = cfg;
9
+ const durationSecs = Math.max(1, Math.round(windowMs / 1000));
10
+ const memoryLimiter = new RateLimiterMemory({
11
+ keyPrefix,
12
+ points: maxRequests,
13
+ duration: durationSecs,
14
+ });
15
+ let limiter;
16
+ if (redisClient) {
17
+ limiter = new RateLimiterRedis({
18
+ storeClient: redisClient,
19
+ keyPrefix,
20
+ points: maxRequests,
21
+ duration: durationSecs,
22
+ blockDuration: 0,
23
+ insuranceLimiter: memoryLimiter,
24
+ });
25
+ }
26
+ else {
27
+ limiter = memoryLimiter;
28
+ }
29
+ // ─── Middleware ──────────────────────────────────────
30
+ async function middleware(req, res, next) {
31
+ try {
32
+ const key = keyExtractor(req);
33
+ const result = await limiter.consume(key);
34
+ res.setHeader("X-RateLimit-Limit", maxRequests);
35
+ res.setHeader("X-RateLimit-Remaining", result.remainingPoints);
36
+ res.setHeader("X-RateLimit-Reset", new Date(Date.now() + result.msBeforeNext).toISOString());
37
+ next();
38
+ }
39
+ catch (rejRes) {
40
+ // Non-rate-limit errors should not block traffic
41
+ if (typeof rejRes?.msBeforeNext !== "number") {
42
+ console.error(`[RateLimit:${keyPrefix}] Unexpected error:`, rejRes?.message || rejRes);
43
+ return next();
44
+ }
45
+ res.setHeader("X-RateLimit-Limit", maxRequests);
46
+ res.setHeader("X-RateLimit-Remaining", 0);
47
+ res.setHeader("Retry-After", Math.ceil(rejRes.msBeforeNext / 1000));
48
+ res.status(429).json({
49
+ success: false,
50
+ message: "Too many requests. Please try again later.",
51
+ });
52
+ }
53
+ }
54
+ return { limiter, middleware };
55
+ }
56
+ // ─── Defaults ───────────────────────────────────────────
57
+ function defaultKeyExtractor(req) {
58
+ return req.headers["x-user-id"] || req.ip || "unknown";
59
+ }
60
+ //# sourceMappingURL=create-rate-limiter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-rate-limiter.js","sourceRoot":"","sources":["../../src/rate-limit/create-rate-limiter.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,EAAE;AACF,4EAA4E;AAC5E,2CAA2C;AAG3C,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAuB,MAAM,uBAAuB,CAAC;AAyBjG,2DAA2D;AAE3D,MAAM,UAAU,iBAAiB,CAAC,MAAuB,EAAE;IACzD,MAAM,EACJ,WAAW,GAAG,GAAG,EACjB,QAAQ,GAAG,MAAM,EACjB,SAAS,GAAG,YAAY,EACxB,WAAW,GAAG,IAAI,EAClB,YAAY,GAAG,mBAAmB,GACnC,GAAG,GAAG,CAAC;IAER,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC;IAE9D,MAAM,aAAa,GAAG,IAAI,iBAAiB,CAAC;QAC1C,SAAS;QACT,MAAM,EAAE,WAAW;QACnB,QAAQ,EAAE,YAAY;KACvB,CAAC,CAAC;IAEH,IAAI,OAA4B,CAAC;IAEjC,IAAI,WAAW,EAAE,CAAC;QAChB,OAAO,GAAG,IAAI,gBAAgB,CAAC;YAC7B,WAAW,EAAE,WAAW;YACxB,SAAS;YACT,MAAM,EAAE,WAAW;YACnB,QAAQ,EAAE,YAAY;YACtB,aAAa,EAAE,CAAC;YAChB,gBAAgB,EAAE,aAAa;SAChC,CAAC,CAAC;IACL,CAAC;SAAM,CAAC;QACN,OAAO,GAAG,aAAa,CAAC;IAC1B,CAAC;IAED,wDAAwD;IAExD,KAAK,UAAU,UAAU,CACvB,GAAY,EACZ,GAAa,EACb,IAAkB;QAElB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;YAC9B,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAE1C,GAAG,CAAC,SAAS,CAAC,mBAAmB,EAAE,WAAW,CAAC,CAAC;YAChD,GAAG,CAAC,SAAS,CAAC,uBAAuB,EAAE,MAAM,CAAC,eAAe,CAAC,CAAC;YAC/D,GAAG,CAAC,SAAS,CACX,mBAAmB,EACnB,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC,WAAW,EAAE,CACzD,CAAC;YACF,IAAI,EAAE,CAAC;QACT,CAAC;QAAC,OAAO,MAAW,EAAE,CAAC;YACrB,iDAAiD;YACjD,IAAI,OAAO,MAAM,EAAE,YAAY,KAAK,QAAQ,EAAE,CAAC;gBAC7C,OAAO,CAAC,KAAK,CAAC,cAAc,SAAS,qBAAqB,EAAE,MAAM,EAAE,OAAO,IAAI,MAAM,CAAC,CAAC;gBACvF,OAAO,IAAI,EAAE,CAAC;YAChB,CAAC;YACD,GAAG,CAAC,SAAS,CAAC,mBAAmB,EAAE,WAAW,CAAC,CAAC;YAChD,GAAG,CAAC,SAAS,CAAC,uBAAuB,EAAE,CAAC,CAAC,CAAC;YAC1C,GAAG,CAAC,SAAS,CAAC,aAAa,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC,CAAC;YACpE,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACnB,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE,4CAA4C;aACtD,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC;AACjC,CAAC;AAED,2DAA2D;AAE3D,SAAS,mBAAmB,CAAC,GAAY;IACvC,OAAQ,GAAG,CAAC,OAAO,CAAC,WAAW,CAAY,IAAI,GAAG,CAAC,EAAE,IAAI,SAAS,CAAC;AACrE,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { createRateLimiter } from "./create-rate-limiter.js";
2
+ export type { RateLimitConfig, RateLimitResult } from "./create-rate-limiter.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rate-limit/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { createRateLimiter } from "./create-rate-limiter.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rate-limit/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC"}
@@ -0,0 +1,33 @@
1
+ import { EventEmitter } from "events";
2
+ export interface SchedulerOptions {
3
+ /** Human-readable name for logging (default: "scheduler") */
4
+ name?: string;
5
+ /** Safety-net fallback interval in ms (default: 60 000) */
6
+ fallbackIntervalMs?: number;
7
+ /** The work function to execute on each trigger */
8
+ work: () => Promise<void> | void;
9
+ /** Logger (default: console) */
10
+ logger?: Pick<Console, "debug" | "info" | "error">;
11
+ }
12
+ export declare class EventDrivenScheduler extends EventEmitter {
13
+ private readonly name;
14
+ private readonly fallbackMs;
15
+ private readonly work;
16
+ private readonly logger;
17
+ private timer;
18
+ private running;
19
+ constructor(options: SchedulerOptions);
20
+ /** Start the safety-net interval. Does not run work immediately. */
21
+ start(): void;
22
+ /** Stop the scheduler and cancel the safety-net interval. */
23
+ stop(): void;
24
+ /**
25
+ * Trigger work immediately (event-driven).
26
+ * De-duplicates: if work is already running, the call is silently skipped.
27
+ */
28
+ trigger(): Promise<void>;
29
+ /** Whether the scheduler is currently executing its work function. */
30
+ get isRunning(): boolean;
31
+ private execute;
32
+ }
33
+ //# sourceMappingURL=event-driven-scheduler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-driven-scheduler.d.ts","sourceRoot":"","sources":["../../src/scheduling/event-driven-scheduler.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAItC,MAAM,WAAW,gBAAgB;IAC/B,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,mDAAmD;IACnD,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACjC,gCAAgC;IAChC,MAAM,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;CACpD;AAID,qBAAa,oBAAqB,SAAQ,YAAY;IACpD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA6B;IAClD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4C;IAEnE,OAAO,CAAC,KAAK,CAA+C;IAC5D,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,EAAE,gBAAgB;IAQrC,oEAAoE;IACpE,KAAK,IAAI,IAAI;IAMb,6DAA6D;IAC7D,IAAI,IAAI,IAAI;IAQZ;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAI9B,sEAAsE;IACtE,IAAI,SAAS,IAAI,OAAO,CAEvB;YAIa,OAAO;CAgBtB"}
@@ -0,0 +1,67 @@
1
+ // pendoadmin-shared-lib/src/scheduling/event-driven-scheduler.ts
2
+ //
3
+ // Replaces naive setInterval polling with an event-driven pattern
4
+ // that still has a safety-net fallback interval.
5
+ import { EventEmitter } from "events";
6
+ // ─── Scheduler ──────────────────────────────────────────
7
+ export class EventDrivenScheduler extends EventEmitter {
8
+ name;
9
+ fallbackMs;
10
+ work;
11
+ logger;
12
+ timer = null;
13
+ running = false;
14
+ constructor(options) {
15
+ super();
16
+ this.name = options.name ?? "scheduler";
17
+ this.fallbackMs = options.fallbackIntervalMs ?? 60_000;
18
+ this.work = options.work;
19
+ this.logger = options.logger ?? console;
20
+ }
21
+ /** Start the safety-net interval. Does not run work immediately. */
22
+ start() {
23
+ if (this.timer)
24
+ return;
25
+ this.logger.info(`[${this.name}] Started (fallback every ${this.fallbackMs}ms)`);
26
+ this.timer = setInterval(() => void this.execute(), this.fallbackMs);
27
+ }
28
+ /** Stop the scheduler and cancel the safety-net interval. */
29
+ stop() {
30
+ if (this.timer) {
31
+ clearInterval(this.timer);
32
+ this.timer = null;
33
+ this.logger.info(`[${this.name}] Stopped`);
34
+ }
35
+ }
36
+ /**
37
+ * Trigger work immediately (event-driven).
38
+ * De-duplicates: if work is already running, the call is silently skipped.
39
+ */
40
+ async trigger() {
41
+ return this.execute();
42
+ }
43
+ /** Whether the scheduler is currently executing its work function. */
44
+ get isRunning() {
45
+ return this.running;
46
+ }
47
+ // ─── Internal ─────────────────────────────────────
48
+ async execute() {
49
+ if (this.running) {
50
+ this.logger.debug(`[${this.name}] Skipped — already running`);
51
+ return;
52
+ }
53
+ this.running = true;
54
+ try {
55
+ await this.work();
56
+ this.emit("done");
57
+ }
58
+ catch (err) {
59
+ this.logger.error(`[${this.name}] Error: ${err.message}`);
60
+ this.emit("error", err);
61
+ }
62
+ finally {
63
+ this.running = false;
64
+ }
65
+ }
66
+ }
67
+ //# sourceMappingURL=event-driven-scheduler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-driven-scheduler.js","sourceRoot":"","sources":["../../src/scheduling/event-driven-scheduler.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,EAAE;AACF,kEAAkE;AAClE,iDAAiD;AAEjD,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAetC,2DAA2D;AAE3D,MAAM,OAAO,oBAAqB,SAAQ,YAAY;IACnC,IAAI,CAAS;IACb,UAAU,CAAS;IACnB,IAAI,CAA6B;IACjC,MAAM,CAA4C;IAE3D,KAAK,GAA0C,IAAI,CAAC;IACpD,OAAO,GAAG,KAAK,CAAC;IAExB,YAAY,OAAyB;QACnC,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,WAAW,CAAC;QACxC,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,kBAAkB,IAAI,MAAM,CAAC;QACvD,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;QACzB,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC;IAC1C,CAAC;IAED,oEAAoE;IACpE,KAAK;QACH,IAAI,IAAI,CAAC,KAAK;YAAE,OAAO;QACvB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,6BAA6B,IAAI,CAAC,UAAU,KAAK,CAAC,CAAC;QACjF,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,CAAC,OAAO,EAAE,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;IACvE,CAAC;IAED,6DAA6D;IAC7D,IAAI;QACF,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC1B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;YAClB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,WAAW,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,OAAO;QACX,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC;IACxB,CAAC;IAED,sEAAsE;IACtE,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED,qDAAqD;IAE7C,KAAK,CAAC,OAAO;QACnB,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,IAAI,6BAA6B,CAAC,CAAC;YAC9D,OAAO;QACT,CAAC;QACD,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;YAClB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACpB,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,IAAI,YAAY,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;YAC1D,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QAC1B,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QACvB,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,3 @@
1
+ export { EventDrivenScheduler } from "./event-driven-scheduler.js";
2
+ export type { SchedulerOptions } from "./event-driven-scheduler.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/scheduling/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,YAAY,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { EventDrivenScheduler } from "./event-driven-scheduler.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/scheduling/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC"}
@@ -0,0 +1,30 @@
1
+ import { Server as HttpServer } from "http";
2
+ import { Server, ServerOptions, Socket } from "socket.io";
3
+ export interface SocketServerOptions {
4
+ /** Room prefix used to namespace broadcasts (default: "company") */
5
+ roomPrefix?: string;
6
+ /** CORS origin list (default: ["*"]) */
7
+ corsOrigins?: string[];
8
+ /** Additional Socket.IO server options */
9
+ serverOptions?: Partial<ServerOptions>;
10
+ /** Logger — any object with debug / info / warn / error (default: console) */
11
+ logger?: Pick<Console, "debug" | "info" | "warn" | "error">;
12
+ /**
13
+ * Authenticate the incoming socket.
14
+ * Return truthy to allow, falsy or throw to reject.
15
+ * The decoded user should be attached to `socket.data.user`.
16
+ */
17
+ authenticate?: (socket: Socket) => Promise<boolean>;
18
+ }
19
+ export interface SocketEmitters {
20
+ /** Raw Socket.IO server instance */
21
+ io: Server;
22
+ /** Send to a specific room */
23
+ emitToRoom: (room: string, event: string, data: unknown) => void;
24
+ /** Send to a company-scoped room (roomPrefix:companyId) */
25
+ emitToCompany: (companyId: string | number, event: string, data: unknown) => void;
26
+ /** Broadcast to all connected clients */
27
+ emitGlobal: (event: string, data: unknown) => void;
28
+ }
29
+ export declare function createSocketServer(httpServer: HttpServer, options?: SocketServerOptions): SocketEmitters;
30
+ //# sourceMappingURL=create-socket-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-socket-server.d.ts","sourceRoot":"","sources":["../../src/socket/create-socket-server.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,MAAM,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAI1D,MAAM,WAAW,mBAAmB;IAClC,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,wCAAwC;IACxC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,0CAA0C;IAC1C,aAAa,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;IACvC,8EAA8E;IAC9E,MAAM,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;IAC5D;;;;OAIG;IACH,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACrD;AAED,MAAM,WAAW,cAAc;IAC7B,oCAAoC;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,8BAA8B;IAC9B,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACjE,2DAA2D;IAC3D,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAClF,yCAAyC;IACzC,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACpD;AAID,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,UAAU,EACtB,OAAO,GAAE,mBAAwB,GAChC,cAAc,CAoEhB"}
@@ -0,0 +1,59 @@
1
+ // pendoadmin-shared-lib/src/socket/create-socket-server.ts
2
+ //
3
+ // Factory that creates a Socket.IO server with company-scoped rooms,
4
+ // authentication, and convenience emitters.
5
+ import { Server } from "socket.io";
6
+ // ─── Factory ────────────────────────────────────────────
7
+ export function createSocketServer(httpServer, options = {}) {
8
+ const { roomPrefix = "company", corsOrigins = ["*"], serverOptions = {}, logger = console, authenticate, } = options;
9
+ const io = new Server(httpServer, {
10
+ cors: { origin: corsOrigins, credentials: true },
11
+ ...serverOptions,
12
+ });
13
+ // ─── Authentication middleware ───────────────────────
14
+ if (authenticate) {
15
+ io.use(async (socket, next) => {
16
+ try {
17
+ const ok = await authenticate(socket);
18
+ if (!ok)
19
+ return next(new Error("Authentication failed"));
20
+ next();
21
+ }
22
+ catch (err) {
23
+ logger.warn(`[Socket] Auth rejected: ${err.message}`);
24
+ next(new Error("Authentication failed"));
25
+ }
26
+ });
27
+ }
28
+ // ─── Connection lifecycle ───────────────────────────
29
+ io.on("connection", (socket) => {
30
+ const userId = socket.data?.user?.id ?? socket.id;
31
+ logger.debug(`[Socket] Connected: ${userId}`);
32
+ // Auto-join the company room when the client provides a companyId
33
+ socket.on("join:company", (companyId) => {
34
+ const room = `${roomPrefix}:${companyId}`;
35
+ socket.join(room);
36
+ logger.debug(`[Socket] ${userId} joined ${room}`);
37
+ });
38
+ socket.on("leave:company", (companyId) => {
39
+ const room = `${roomPrefix}:${companyId}`;
40
+ socket.leave(room);
41
+ logger.debug(`[Socket] ${userId} left ${room}`);
42
+ });
43
+ socket.on("disconnect", (reason) => {
44
+ logger.debug(`[Socket] Disconnected: ${userId} (${reason})`);
45
+ });
46
+ });
47
+ // ─── Emitter helpers ────────────────────────────────
48
+ function emitToRoom(room, event, data) {
49
+ io.to(room).emit(event, data);
50
+ }
51
+ function emitToCompany(companyId, event, data) {
52
+ emitToRoom(`${roomPrefix}:${companyId}`, event, data);
53
+ }
54
+ function emitGlobal(event, data) {
55
+ io.emit(event, data);
56
+ }
57
+ return { io, emitToRoom, emitToCompany, emitGlobal };
58
+ }
59
+ //# sourceMappingURL=create-socket-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-socket-server.js","sourceRoot":"","sources":["../../src/socket/create-socket-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export { createSocketServer, } from "./create-socket-server.js";
2
+ export type { SocketServerOptions, SocketEmitters, } from "./create-socket-server.js";
3
+ export { BaseSocketGateway, } from "./nestjs-socket.module.js";
4
+ export type { NestSocketOptions, } from "./nestjs-socket.module.js";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/socket/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,GACnB,MAAM,2BAA2B,CAAC;AACnC,YAAY,EACV,mBAAmB,EACnB,cAAc,GACf,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,iBAAiB,GAClB,MAAM,2BAA2B,CAAC;AACnC,YAAY,EACV,iBAAiB,GAClB,MAAM,2BAA2B,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { createSocketServer, } from "./create-socket-server.js";
2
+ export { BaseSocketGateway, } from "./nestjs-socket.module.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/socket/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,GACnB,MAAM,2BAA2B,CAAC;AAMnC,OAAO,EACL,iBAAiB,GAClB,MAAM,2BAA2B,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { OnGatewayConnection, OnGatewayDisconnect } from "@nestjs/websockets";
2
+ import { Server, Socket } from "socket.io";
3
+ export interface NestSocketOptions {
4
+ /** Room prefix used to namespace broadcasts (default: "company") */
5
+ roomPrefix?: string;
6
+ /** Logger — any object with debug / info / warn / error (default: console) */
7
+ logger?: Pick<Console, "debug" | "info" | "warn" | "error">;
8
+ }
9
+ export declare abstract class BaseSocketGateway implements OnGatewayConnection, OnGatewayDisconnect {
10
+ server: Server;
11
+ protected readonly roomPrefix: string;
12
+ protected readonly logger: Pick<Console, "debug" | "info" | "warn" | "error">;
13
+ constructor(options?: NestSocketOptions);
14
+ handleConnection(client: Socket): void;
15
+ handleDisconnect(client: Socket): void;
16
+ emitToRoom(room: string, event: string, data: unknown): void;
17
+ emitToCompany(companyId: string | number, event: string, data: unknown): void;
18
+ emitGlobal(event: string, data: unknown): void;
19
+ }
20
+ //# sourceMappingURL=nestjs-socket.module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nestjs-socket.module.d.ts","sourceRoot":"","sources":["../../src/socket/nestjs-socket.module.ts"],"names":[],"mappings":"AAKA,OAAO,EAEL,mBAAmB,EACnB,mBAAmB,EAEpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAI3C,MAAM,WAAW,iBAAiB;IAChC,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8EAA8E;IAC9E,MAAM,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;CAC7D;AAID,8BACsB,iBACpB,YAAW,mBAAmB,EAAE,mBAAmB;IAGnD,MAAM,EAAG,MAAM,CAAC;IAEhB,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IACtC,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;gBAElE,OAAO,GAAE,iBAAsB;IAO3C,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAiBtC,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAOtC,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI;IAI5D,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI;IAI7E,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI;CAG/C"}
@@ -0,0 +1,64 @@
1
+ // pendoadmin-shared-lib/src/socket/nestjs-socket.module.ts
2
+ //
3
+ // Abstract base gateway for NestJS microservices that use Socket.IO.
4
+ // Provides room management and typed emit helpers.
5
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
6
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
7
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
8
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
9
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
10
+ };
11
+ var __metadata = (this && this.__metadata) || function (k, v) {
12
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
13
+ };
14
+ import { WebSocketGateway, WebSocketServer, } from "@nestjs/websockets";
15
+ import { Server } from "socket.io";
16
+ // ─── Base Gateway ───────────────────────────────────────
17
+ let BaseSocketGateway = class BaseSocketGateway {
18
+ server;
19
+ roomPrefix;
20
+ logger;
21
+ constructor(options = {}) {
22
+ this.roomPrefix = options.roomPrefix ?? "company";
23
+ this.logger = options.logger ?? console;
24
+ }
25
+ // ─── Lifecycle ────────────────────────────────────
26
+ handleConnection(client) {
27
+ const userId = client.data?.user?.id ?? client.id;
28
+ this.logger.debug(`[WS] Connected: ${userId}`);
29
+ client.on("join:company", (companyId) => {
30
+ const room = `${this.roomPrefix}:${companyId}`;
31
+ client.join(room);
32
+ this.logger.debug(`[WS] ${userId} joined ${room}`);
33
+ });
34
+ client.on("leave:company", (companyId) => {
35
+ const room = `${this.roomPrefix}:${companyId}`;
36
+ client.leave(room);
37
+ this.logger.debug(`[WS] ${userId} left ${room}`);
38
+ });
39
+ }
40
+ handleDisconnect(client) {
41
+ const userId = client.data?.user?.id ?? client.id;
42
+ this.logger.debug(`[WS] Disconnected: ${userId}`);
43
+ }
44
+ // ─── Emit helpers ─────────────────────────────────
45
+ emitToRoom(room, event, data) {
46
+ this.server.to(room).emit(event, data);
47
+ }
48
+ emitToCompany(companyId, event, data) {
49
+ this.emitToRoom(`${this.roomPrefix}:${companyId}`, event, data);
50
+ }
51
+ emitGlobal(event, data) {
52
+ this.server.emit(event, data);
53
+ }
54
+ };
55
+ __decorate([
56
+ WebSocketServer(),
57
+ __metadata("design:type", Server)
58
+ ], BaseSocketGateway.prototype, "server", void 0);
59
+ BaseSocketGateway = __decorate([
60
+ WebSocketGateway({ cors: { origin: "*" } }),
61
+ __metadata("design:paramtypes", [Object])
62
+ ], BaseSocketGateway);
63
+ export { BaseSocketGateway };
64
+ //# sourceMappingURL=nestjs-socket.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nestjs-socket.module.js","sourceRoot":"","sources":["../../src/socket/nestjs-socket.module.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,EAAE;AACF,qEAAqE;AACrE,mDAAmD;;;;;;;;;;AAEnD,OAAO,EACL,gBAAgB,EAGhB,eAAe,GAChB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,MAAM,EAAU,MAAM,WAAW,CAAC;AAW3C,2DAA2D;AAGpD,IAAe,iBAAiB,GAAhC,MAAe,iBAAiB;IAIrC,MAAM,CAAU;IAEG,UAAU,CAAS;IACnB,MAAM,CAAqD;IAE9E,YAAY,UAA6B,EAAE;QACzC,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,SAAS,CAAC;QAClD,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC;IAC1C,CAAC;IAED,qDAAqD;IAErD,gBAAgB,CAAC,MAAc;QAC7B,MAAM,MAAM,GAAI,MAAc,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,IAAI,MAAM,CAAC,EAAE,CAAC;QAC3D,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,mBAAmB,MAAM,EAAE,CAAC,CAAC;QAE/C,MAAM,CAAC,EAAE,CAAC,cAAc,EAAE,CAAC,SAA0B,EAAE,EAAE;YACvD,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,UAAU,IAAI,SAAS,EAAE,CAAC;YAC/C,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,MAAM,WAAW,IAAI,EAAE,CAAC,CAAC;QACrD,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE,CAAC,eAAe,EAAE,CAAC,SAA0B,EAAE,EAAE;YACxD,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,UAAU,IAAI,SAAS,EAAE,CAAC;YAC/C,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACnB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,MAAM,SAAS,IAAI,EAAE,CAAC,CAAC;QACnD,CAAC,CAAC,CAAC;IACL,CAAC;IAED,gBAAgB,CAAC,MAAc;QAC7B,MAAM,MAAM,GAAI,MAAc,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,IAAI,MAAM,CAAC,EAAE,CAAC;QAC3D,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,sBAAsB,MAAM,EAAE,CAAC,CAAC;IACpD,CAAC;IAED,qDAAqD;IAErD,UAAU,CAAC,IAAY,EAAE,KAAa,EAAE,IAAa;QACnD,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACzC,CAAC;IAED,aAAa,CAAC,SAA0B,EAAE,KAAa,EAAE,IAAa;QACpE,IAAI,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC,UAAU,IAAI,SAAS,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;IAClE,CAAC;IAED,UAAU,CAAC,KAAa,EAAE,IAAa;QACrC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAChC,CAAC;CACF,CAAA;AA/CC;IADC,eAAe,EAAE;8BACT,MAAM;iDAAC;AAJI,iBAAiB;IADtC,gBAAgB,CAAC,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC;;GACtB,iBAAiB,CAmDtC"}
@@ -0,0 +1,25 @@
1
+ import { SQL } from "drizzle-orm";
2
+ import type { Request, Response, NextFunction } from "express";
3
+ export interface TenantWhereHelper {
4
+ /** Standalone tenant filter — e.g. db.select().where(tw.where) */
5
+ where: SQL;
6
+ /** Combine tenant filter with additional conditions */
7
+ and: (...conditions: SQL[]) => SQL;
8
+ }
9
+ /**
10
+ * Create a reusable WHERE helper scoped to a single company.
11
+ *
12
+ * @param companyIdColumn - The Drizzle column reference (e.g. `users.companyId`)
13
+ * @param companyId - The company / tenant ID value
14
+ */
15
+ export declare function createTenantWhere(companyIdColumn: any, companyId: string | number): TenantWhereHelper;
16
+ /**
17
+ * Express middleware that extracts companyId from `req.user` (set by
18
+ * auth middleware) and attaches a `tenantWhere` helper to the request.
19
+ *
20
+ * Usage:
21
+ * app.use(tenantMiddleware(schema.users.companyId));
22
+ * // Then in handlers: req.tenantWhere.where or req.tenantWhere.and(...)
23
+ */
24
+ export declare function tenantMiddleware(companyIdColumn: any): (req: Request, _res: Response, next: NextFunction) => void;
25
+ //# sourceMappingURL=drizzle-tenant.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drizzle-tenant.d.ts","sourceRoot":"","sources":["../../src/tenant/drizzle-tenant.ts"],"names":[],"mappings":"AAIA,OAAO,EAAW,GAAG,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAI/D,MAAM,WAAW,iBAAiB;IAChC,kEAAkE;IAClE,KAAK,EAAE,GAAG,CAAC;IACX,uDAAuD;IACvD,GAAG,EAAE,CAAC,GAAG,UAAU,EAAE,GAAG,EAAE,KAAK,GAAG,CAAC;CACpC;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,eAAe,EAAE,GAAG,EACpB,SAAS,EAAE,MAAM,GAAG,MAAM,GACzB,iBAAiB,CAOnB;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,eAAe,EAAE,GAAG,IAC3C,KAAK,OAAO,EAAE,MAAM,QAAQ,EAAE,MAAM,YAAY,UAOzD"}
@@ -0,0 +1,35 @@
1
+ // pendoadmin-shared-lib/src/tenant/drizzle-tenant.ts
2
+ //
3
+ // Drizzle ORM helper that builds reusable tenant WHERE clauses.
4
+ import { eq, and } from "drizzle-orm";
5
+ /**
6
+ * Create a reusable WHERE helper scoped to a single company.
7
+ *
8
+ * @param companyIdColumn - The Drizzle column reference (e.g. `users.companyId`)
9
+ * @param companyId - The company / tenant ID value
10
+ */
11
+ export function createTenantWhere(companyIdColumn, companyId) {
12
+ const tenantFilter = eq(companyIdColumn, companyId);
13
+ return {
14
+ where: tenantFilter,
15
+ and: (...conditions) => and(tenantFilter, ...conditions),
16
+ };
17
+ }
18
+ /**
19
+ * Express middleware that extracts companyId from `req.user` (set by
20
+ * auth middleware) and attaches a `tenantWhere` helper to the request.
21
+ *
22
+ * Usage:
23
+ * app.use(tenantMiddleware(schema.users.companyId));
24
+ * // Then in handlers: req.tenantWhere.where or req.tenantWhere.and(...)
25
+ */
26
+ export function tenantMiddleware(companyIdColumn) {
27
+ return (req, _res, next) => {
28
+ const companyId = req.user?.company_id;
29
+ if (companyId) {
30
+ req.tenantWhere = createTenantWhere(companyIdColumn, companyId);
31
+ }
32
+ next();
33
+ };
34
+ }
35
+ //# sourceMappingURL=drizzle-tenant.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drizzle-tenant.js","sourceRoot":"","sources":["../../src/tenant/drizzle-tenant.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,EAAE;AACF,gEAAgE;AAEhE,OAAO,EAAE,EAAE,EAAE,GAAG,EAAO,MAAM,aAAa,CAAC;AAY3C;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAC/B,eAAoB,EACpB,SAA0B;IAE1B,MAAM,YAAY,GAAG,EAAE,CAAC,eAAe,EAAE,SAAS,CAAC,CAAC;IAEpD,OAAO;QACL,KAAK,EAAE,YAAY;QACnB,GAAG,EAAE,CAAC,GAAG,UAAiB,EAAE,EAAE,CAAC,GAAG,CAAC,YAAY,EAAE,GAAG,UAAU,CAAE;KACjE,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,eAAoB;IACnD,OAAO,CAAC,GAAY,EAAE,IAAc,EAAE,IAAkB,EAAE,EAAE;QAC1D,MAAM,SAAS,GAAI,GAAW,CAAC,IAAI,EAAE,UAAU,CAAC;QAChD,IAAI,SAAS,EAAE,CAAC;YACb,GAAW,CAAC,WAAW,GAAG,iBAAiB,CAAC,eAAe,EAAE,SAAS,CAAC,CAAC;QAC3E,CAAC;QACD,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,7 @@
1
+ export { scopedKnex } from "./knex-tenant.js";
2
+ export type { ScopedKnexOptions } from "./knex-tenant.js";
3
+ export { createTenantWhere, tenantMiddleware } from "./drizzle-tenant.js";
4
+ export type { TenantWhereHelper } from "./drizzle-tenant.js";
5
+ export { createTenantAccessControl, knexFeatureResolver, knexCompanyActiveChecker, getFeaturesForPlan, provisionCompanyPlan, clearFeatureCache, invalidateCompanyFeatureCache, DEFAULT_PLANS, } from "./tenant-access-control.js";
6
+ export type { Feature, SubscriptionPlan, TenantAccessControlConfig, } from "./tenant-access-control.js";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tenant/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAG1D,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC1E,YAAY,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAG7D,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,wBAAwB,EACxB,kBAAkB,EAClB,oBAAoB,EACpB,iBAAiB,EACjB,6BAA6B,EAC7B,aAAa,GACd,MAAM,4BAA4B,CAAC;AACpC,YAAY,EACV,OAAO,EACP,gBAAgB,EAChB,yBAAyB,GAC1B,MAAM,4BAA4B,CAAC"}
@@ -0,0 +1,7 @@
1
+ // Knex tenant scoping
2
+ export { scopedKnex } from "./knex-tenant.js";
3
+ // Drizzle tenant scoping
4
+ export { createTenantWhere, tenantMiddleware } from "./drizzle-tenant.js";
5
+ // Tenant access control (feature gating)
6
+ export { createTenantAccessControl, knexFeatureResolver, knexCompanyActiveChecker, getFeaturesForPlan, provisionCompanyPlan, clearFeatureCache, invalidateCompanyFeatureCache, DEFAULT_PLANS, } from "./tenant-access-control.js";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tenant/index.ts"],"names":[],"mappings":"AAAA,sBAAsB;AACtB,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAG9C,yBAAyB;AACzB,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAG1E,yCAAyC;AACzC,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,wBAAwB,EACxB,kBAAkB,EAClB,oBAAoB,EACpB,iBAAiB,EACjB,6BAA6B,EAC7B,aAAa,GACd,MAAM,4BAA4B,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { Knex } from "knex";
2
+ export interface ScopedKnexOptions {
3
+ /** Column name for tenant isolation (default: "company_id") */
4
+ tenantColumn?: string;
5
+ /** Tables to exclude from automatic scoping */
6
+ excludeTables?: string[];
7
+ }
8
+ /**
9
+ * Returns a Proxy around `knex` that intercepts table calls and
10
+ * injects `.where(tenantColumn, companyId)`.
11
+ */
12
+ export declare function scopedKnex(knex: Knex, companyId: string | number, options?: ScopedKnexOptions): Knex;
13
+ //# sourceMappingURL=knex-tenant.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"knex-tenant.d.ts","sourceRoot":"","sources":["../../src/tenant/knex-tenant.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAEjC,MAAM,WAAW,iBAAiB;IAChC,+DAA+D;IAC/D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,+CAA+C;IAC/C,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED;;;GAGG;AACH,wBAAgB,UAAU,CACxB,IAAI,EAAE,IAAI,EACV,SAAS,EAAE,MAAM,GAAG,MAAM,EAC1B,OAAO,GAAE,iBAAsB,GAC9B,IAAI,CAoCN"}