nestjs-tenant-shield 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 (167) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +612 -0
  3. package/dist/cache/cache.registry.d.ts +19 -0
  4. package/dist/cache/cache.registry.d.ts.map +1 -0
  5. package/dist/cache/cache.registry.js +51 -0
  6. package/dist/cache/cache.registry.js.map +1 -0
  7. package/dist/cache/cache.service.d.ts +44 -0
  8. package/dist/cache/cache.service.d.ts.map +1 -0
  9. package/dist/cache/cache.service.js +64 -0
  10. package/dist/cache/cache.service.js.map +1 -0
  11. package/dist/cache/index.d.ts +3 -0
  12. package/dist/cache/index.d.ts.map +1 -0
  13. package/dist/cache/index.js +19 -0
  14. package/dist/cache/index.js.map +1 -0
  15. package/dist/constants/index.d.ts +64 -0
  16. package/dist/constants/index.d.ts.map +1 -0
  17. package/dist/constants/index.js +67 -0
  18. package/dist/constants/index.js.map +1 -0
  19. package/dist/context/get-current-tenant-id.d.ts +30 -0
  20. package/dist/context/get-current-tenant-id.d.ts.map +1 -0
  21. package/dist/context/get-current-tenant-id.js +40 -0
  22. package/dist/context/get-current-tenant-id.js.map +1 -0
  23. package/dist/context/index.d.ts +7 -0
  24. package/dist/context/index.d.ts.map +1 -0
  25. package/dist/context/index.js +23 -0
  26. package/dist/context/index.js.map +1 -0
  27. package/dist/context/run-with-tenant.d.ts +84 -0
  28. package/dist/context/run-with-tenant.d.ts.map +1 -0
  29. package/dist/context/run-with-tenant.js +95 -0
  30. package/dist/context/run-with-tenant.js.map +1 -0
  31. package/dist/context/tenant-context.storage.d.ts +43 -0
  32. package/dist/context/tenant-context.storage.d.ts.map +1 -0
  33. package/dist/context/tenant-context.storage.js +45 -0
  34. package/dist/context/tenant-context.storage.js.map +1 -0
  35. package/dist/decorators/cacheable.decorator.d.ts +27 -0
  36. package/dist/decorators/cacheable.decorator.d.ts.map +1 -0
  37. package/dist/decorators/cacheable.decorator.js +108 -0
  38. package/dist/decorators/cacheable.decorator.js.map +1 -0
  39. package/dist/decorators/index.d.ts +13 -0
  40. package/dist/decorators/index.d.ts.map +1 -0
  41. package/dist/decorators/index.js +29 -0
  42. package/dist/decorators/index.js.map +1 -0
  43. package/dist/decorators/require-tenant.decorator.d.ts +41 -0
  44. package/dist/decorators/require-tenant.decorator.d.ts.map +1 -0
  45. package/dist/decorators/require-tenant.decorator.js +125 -0
  46. package/dist/decorators/require-tenant.decorator.js.map +1 -0
  47. package/dist/decorators/system-action.decorator.d.ts +39 -0
  48. package/dist/decorators/system-action.decorator.d.ts.map +1 -0
  49. package/dist/decorators/system-action.decorator.js +50 -0
  50. package/dist/decorators/system-action.decorator.js.map +1 -0
  51. package/dist/decorators/tenant-context.decorator.d.ts +33 -0
  52. package/dist/decorators/tenant-context.decorator.d.ts.map +1 -0
  53. package/dist/decorators/tenant-context.decorator.js +54 -0
  54. package/dist/decorators/tenant-context.decorator.js.map +1 -0
  55. package/dist/errors/cross-tenant-access.error.d.ts +29 -0
  56. package/dist/errors/cross-tenant-access.error.d.ts.map +1 -0
  57. package/dist/errors/cross-tenant-access.error.js +37 -0
  58. package/dist/errors/cross-tenant-access.error.js.map +1 -0
  59. package/dist/errors/index.d.ts +10 -0
  60. package/dist/errors/index.d.ts.map +1 -0
  61. package/dist/errors/index.js +26 -0
  62. package/dist/errors/index.js.map +1 -0
  63. package/dist/errors/invalid-tenant-source.error.d.ts +20 -0
  64. package/dist/errors/invalid-tenant-source.error.d.ts.map +1 -0
  65. package/dist/errors/invalid-tenant-source.error.js +28 -0
  66. package/dist/errors/invalid-tenant-source.error.js.map +1 -0
  67. package/dist/errors/missing-tenant-context.error.d.ts +22 -0
  68. package/dist/errors/missing-tenant-context.error.d.ts.map +1 -0
  69. package/dist/errors/missing-tenant-context.error.js +32 -0
  70. package/dist/errors/missing-tenant-context.error.js.map +1 -0
  71. package/dist/index.d.ts +36 -0
  72. package/dist/index.d.ts.map +1 -0
  73. package/dist/index.js +56 -0
  74. package/dist/index.js.map +1 -0
  75. package/dist/interfaces/cacheable-options.interface.d.ts +37 -0
  76. package/dist/interfaces/cacheable-options.interface.d.ts.map +1 -0
  77. package/dist/interfaces/cacheable-options.interface.js +3 -0
  78. package/dist/interfaces/cacheable-options.interface.js.map +1 -0
  79. package/dist/interfaces/index.d.ts +13 -0
  80. package/dist/interfaces/index.d.ts.map +1 -0
  81. package/dist/interfaces/index.js +30 -0
  82. package/dist/interfaces/index.js.map +1 -0
  83. package/dist/interfaces/require-tenant-options.interface.d.ts +23 -0
  84. package/dist/interfaces/require-tenant-options.interface.d.ts.map +1 -0
  85. package/dist/interfaces/require-tenant-options.interface.js +3 -0
  86. package/dist/interfaces/require-tenant-options.interface.js.map +1 -0
  87. package/dist/interfaces/tenant-context-options.interface.d.ts +19 -0
  88. package/dist/interfaces/tenant-context-options.interface.d.ts.map +1 -0
  89. package/dist/interfaces/tenant-context-options.interface.js +3 -0
  90. package/dist/interfaces/tenant-context-options.interface.js.map +1 -0
  91. package/dist/interfaces/tenant-context.interface.d.ts +26 -0
  92. package/dist/interfaces/tenant-context.interface.d.ts.map +1 -0
  93. package/dist/interfaces/tenant-context.interface.js +3 -0
  94. package/dist/interfaces/tenant-context.interface.js.map +1 -0
  95. package/dist/interfaces/tenant-shield-options.interface.d.ts +141 -0
  96. package/dist/interfaces/tenant-shield-options.interface.d.ts.map +1 -0
  97. package/dist/interfaces/tenant-shield-options.interface.js +11 -0
  98. package/dist/interfaces/tenant-shield-options.interface.js.map +1 -0
  99. package/dist/middleware/index.d.ts +2 -0
  100. package/dist/middleware/index.d.ts.map +1 -0
  101. package/dist/middleware/index.js +18 -0
  102. package/dist/middleware/index.js.map +1 -0
  103. package/dist/middleware/tenant-context.middleware.d.ts +30 -0
  104. package/dist/middleware/tenant-context.middleware.d.ts.map +1 -0
  105. package/dist/middleware/tenant-context.middleware.js +68 -0
  106. package/dist/middleware/tenant-context.middleware.js.map +1 -0
  107. package/dist/options/index.d.ts +2 -0
  108. package/dist/options/index.d.ts.map +1 -0
  109. package/dist/options/index.js +18 -0
  110. package/dist/options/index.js.map +1 -0
  111. package/dist/options/options.registry.d.ts +8 -0
  112. package/dist/options/options.registry.d.ts.map +1 -0
  113. package/dist/options/options.registry.js +36 -0
  114. package/dist/options/options.registry.js.map +1 -0
  115. package/dist/resolvers/custom.resolver.d.ts +29 -0
  116. package/dist/resolvers/custom.resolver.d.ts.map +1 -0
  117. package/dist/resolvers/custom.resolver.js +47 -0
  118. package/dist/resolvers/custom.resolver.js.map +1 -0
  119. package/dist/resolvers/header.resolver.d.ts +22 -0
  120. package/dist/resolvers/header.resolver.d.ts.map +1 -0
  121. package/dist/resolvers/header.resolver.js +39 -0
  122. package/dist/resolvers/header.resolver.js.map +1 -0
  123. package/dist/resolvers/index.d.ts +13 -0
  124. package/dist/resolvers/index.d.ts.map +1 -0
  125. package/dist/resolvers/index.js +29 -0
  126. package/dist/resolvers/index.js.map +1 -0
  127. package/dist/resolvers/jwt.resolver.d.ts +35 -0
  128. package/dist/resolvers/jwt.resolver.d.ts.map +1 -0
  129. package/dist/resolvers/jwt.resolver.js +51 -0
  130. package/dist/resolvers/jwt.resolver.js.map +1 -0
  131. package/dist/resolvers/resolver.factory.d.ts +12 -0
  132. package/dist/resolvers/resolver.factory.d.ts.map +1 -0
  133. package/dist/resolvers/resolver.factory.js +43 -0
  134. package/dist/resolvers/resolver.factory.js.map +1 -0
  135. package/dist/resolvers/subdomain.resolver.d.ts +37 -0
  136. package/dist/resolvers/subdomain.resolver.d.ts.map +1 -0
  137. package/dist/resolvers/subdomain.resolver.js +57 -0
  138. package/dist/resolvers/subdomain.resolver.js.map +1 -0
  139. package/dist/resolvers/tenant-resolver.interface.d.ts +22 -0
  140. package/dist/resolvers/tenant-resolver.interface.d.ts.map +1 -0
  141. package/dist/resolvers/tenant-resolver.interface.js +3 -0
  142. package/dist/resolvers/tenant-resolver.interface.js.map +1 -0
  143. package/dist/tenant-shield.module.d.ts +88 -0
  144. package/dist/tenant-shield.module.d.ts.map +1 -0
  145. package/dist/tenant-shield.module.js +263 -0
  146. package/dist/tenant-shield.module.js.map +1 -0
  147. package/dist/testing/index.d.ts +12 -0
  148. package/dist/testing/index.d.ts.map +1 -0
  149. package/dist/testing/index.js +28 -0
  150. package/dist/testing/index.js.map +1 -0
  151. package/dist/testing/test-helpers.d.ts +52 -0
  152. package/dist/testing/test-helpers.d.ts.map +1 -0
  153. package/dist/testing/test-helpers.js +72 -0
  154. package/dist/testing/test-helpers.js.map +1 -0
  155. package/dist/typeorm/index.d.ts +10 -0
  156. package/dist/typeorm/index.d.ts.map +1 -0
  157. package/dist/typeorm/index.js +26 -0
  158. package/dist/typeorm/index.js.map +1 -0
  159. package/dist/typeorm/raw-sql.helper.d.ts +35 -0
  160. package/dist/typeorm/raw-sql.helper.d.ts.map +1 -0
  161. package/dist/typeorm/raw-sql.helper.js +24 -0
  162. package/dist/typeorm/raw-sql.helper.js.map +1 -0
  163. package/dist/typeorm/tenant.subscriber.d.ts +61 -0
  164. package/dist/typeorm/tenant.subscriber.d.ts.map +1 -0
  165. package/dist/typeorm/tenant.subscriber.js +487 -0
  166. package/dist/typeorm/tenant.subscriber.js.map +1 -0
  167. package/package.json +109 -0
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ /**
18
+ * 테스트 전용 헬퍼 배럴.
19
+ *
20
+ * 외부에서:
21
+ * import { runWithTenant, expectCurrentTenant } from 'nestjs-tenant-shield/testing';
22
+ *
23
+ * 같은 형태로 가져다 쓸 수 있게 합니다.
24
+ *
25
+ * (subpath import 지원은 package.json "exports" 필드 설정 후 활성화 — v0.1.x 예정)
26
+ */
27
+ __exportStar(require("./test-helpers"), exports);
28
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA;;;;;;;;;GASG;AACH,iDAA+B"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * ─────────────────────────────────────────────────────────────
3
+ * 라이브러리 사용자가 테스트 코드에서 쓰는 헬퍼 모음.
4
+ *
5
+ * 핵심 철학:
6
+ * "다른 tenant의 데이터를 실수로 만지는 일을, 테스트 단계에서부터
7
+ * 절대 일어나지 않게 강제한다."
8
+ *
9
+ * runWithTenant / runWithoutTenant 는 이미 src/context/에 정의되어 있고
10
+ * 여기서는 그대로 재-export 합니다 (사용자 import 경로 통일).
11
+ *
12
+ * 추가로 테스트 전용 헬퍼:
13
+ * - expectCurrentTenant : 현재 컨텍스트의 tenant ID 단정
14
+ * - clearTenantContext : 테스트 간 컨텍스트 강제 초기화 (특수 케이스)
15
+ * - mockTenantContext : 동기적으로 컨텍스트만 깔아두기
16
+ *
17
+ * ─────────────────────────────────────────────────────────────
18
+ *
19
+ * [기본 사용 패턴]
20
+ *
21
+ * describe('StudentsService', () => {
22
+ * it('A 학원 컨텍스트에서는 A 학생만 조회', async () => {
23
+ * await runWithTenant('academy-A', async () => {
24
+ * const students = await service.findAll();
25
+ * expect(students.every(s => s.tenantId === 'academy-A')).toBe(true);
26
+ * });
27
+ * });
28
+ *
29
+ * it('컨텍스트 없이 호출하면 throw', async () => {
30
+ * await expect(service.findAll()).rejects.toThrow(MissingTenantContextError);
31
+ * });
32
+ * });
33
+ *
34
+ * ─────────────────────────────────────────────────────────────
35
+ */
36
+ export { resetGlobalOptions } from '../options/options.registry';
37
+ export { resetGlobalCache } from '../cache/cache.registry';
38
+ /**
39
+ * 현재 AsyncLocalStorage에 깔린 tenant ID가 예상과 같은지 단정.
40
+ *
41
+ * Jest assertion API와 호환되도록 throw 기반으로 작성.
42
+ * (expect(...).toBe 대신 직접 던지는 이유 — Jest matcher import 없이 동작)
43
+ */
44
+ export declare function expectCurrentTenant(expected: string | null): void;
45
+ /**
46
+ * 동기적으로 임시 tenant 컨텍스트를 깔고 콜백을 실행.
47
+ *
48
+ * 비동기 콜백을 쓸 수 없는 특수한 동기 헬퍼/유틸을 테스트할 때만 사용.
49
+ * 일반 케이스는 항상 runWithTenant(비동기 버전)를 우선 사용하세요.
50
+ */
51
+ export declare function mockTenantContext<T>(tenantId: string, fn: () => T): T;
52
+ //# sourceMappingURL=test-helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-helpers.d.ts","sourceRoot":"","sources":["../../src/testing/test-helpers.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAOH,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE3D;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAQjE;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAErE"}
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resetGlobalCache = exports.resetGlobalOptions = void 0;
4
+ exports.expectCurrentTenant = expectCurrentTenant;
5
+ exports.mockTenantContext = mockTenantContext;
6
+ const tenant_context_storage_1 = require("../context/tenant-context.storage");
7
+ /**
8
+ * ─────────────────────────────────────────────────────────────
9
+ * 라이브러리 사용자가 테스트 코드에서 쓰는 헬퍼 모음.
10
+ *
11
+ * 핵심 철학:
12
+ * "다른 tenant의 데이터를 실수로 만지는 일을, 테스트 단계에서부터
13
+ * 절대 일어나지 않게 강제한다."
14
+ *
15
+ * runWithTenant / runWithoutTenant 는 이미 src/context/에 정의되어 있고
16
+ * 여기서는 그대로 재-export 합니다 (사용자 import 경로 통일).
17
+ *
18
+ * 추가로 테스트 전용 헬퍼:
19
+ * - expectCurrentTenant : 현재 컨텍스트의 tenant ID 단정
20
+ * - clearTenantContext : 테스트 간 컨텍스트 강제 초기화 (특수 케이스)
21
+ * - mockTenantContext : 동기적으로 컨텍스트만 깔아두기
22
+ *
23
+ * ─────────────────────────────────────────────────────────────
24
+ *
25
+ * [기본 사용 패턴]
26
+ *
27
+ * describe('StudentsService', () => {
28
+ * it('A 학원 컨텍스트에서는 A 학생만 조회', async () => {
29
+ * await runWithTenant('academy-A', async () => {
30
+ * const students = await service.findAll();
31
+ * expect(students.every(s => s.tenantId === 'academy-A')).toBe(true);
32
+ * });
33
+ * });
34
+ *
35
+ * it('컨텍스트 없이 호출하면 throw', async () => {
36
+ * await expect(service.findAll()).rejects.toThrow(MissingTenantContextError);
37
+ * });
38
+ * });
39
+ *
40
+ * ─────────────────────────────────────────────────────────────
41
+ */
42
+ // runWithTenant / runWithoutTenant 는 'nestjs-tenant-shield'의 메인 진입점
43
+ // (src/context를 통해 자동 export) 에서 그대로 가져다 쓰면 됩니다.
44
+ // 여기서 또 export 하면 중복 export 충돌이 발생하므로 제외했습니다.
45
+ // forRoot 없이 단위 테스트할 때 afterEach에서 전역 상태 리셋.
46
+ var options_registry_1 = require("../options/options.registry");
47
+ Object.defineProperty(exports, "resetGlobalOptions", { enumerable: true, get: function () { return options_registry_1.resetGlobalOptions; } });
48
+ var cache_registry_1 = require("../cache/cache.registry");
49
+ Object.defineProperty(exports, "resetGlobalCache", { enumerable: true, get: function () { return cache_registry_1.resetGlobalCache; } });
50
+ /**
51
+ * 현재 AsyncLocalStorage에 깔린 tenant ID가 예상과 같은지 단정.
52
+ *
53
+ * Jest assertion API와 호환되도록 throw 기반으로 작성.
54
+ * (expect(...).toBe 대신 직접 던지는 이유 — Jest matcher import 없이 동작)
55
+ */
56
+ function expectCurrentTenant(expected) {
57
+ const actual = tenant_context_storage_1.tenantContextStorage.getStore()?.tenantId ?? null;
58
+ if (actual !== expected) {
59
+ throw new Error(`expectCurrentTenant: expected "${expected}" but got "${actual}". ` +
60
+ '테스트 셋업의 runWithTenant 호출 위치를 확인하세요.');
61
+ }
62
+ }
63
+ /**
64
+ * 동기적으로 임시 tenant 컨텍스트를 깔고 콜백을 실행.
65
+ *
66
+ * 비동기 콜백을 쓸 수 없는 특수한 동기 헬퍼/유틸을 테스트할 때만 사용.
67
+ * 일반 케이스는 항상 runWithTenant(비동기 버전)를 우선 사용하세요.
68
+ */
69
+ function mockTenantContext(tenantId, fn) {
70
+ return tenant_context_storage_1.tenantContextStorage.run({ tenantId, isSystemAction: false }, fn);
71
+ }
72
+ //# sourceMappingURL=test-helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-helpers.js","sourceRoot":"","sources":["../../src/testing/test-helpers.ts"],"names":[],"mappings":";;;AAoDA,kDAQC;AAQD,8CAEC;AAtED,8EAAyE;AAEzE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,oEAAoE;AACpE,iDAAiD;AACjD,8CAA8C;AAE9C,6CAA6C;AAC7C,gEAAiE;AAAxD,sHAAA,kBAAkB,OAAA;AAC3B,0DAA2D;AAAlD,kHAAA,gBAAgB,OAAA;AAEzB;;;;;GAKG;AACH,SAAgB,mBAAmB,CAAC,QAAuB;IACzD,MAAM,MAAM,GAAG,6CAAoB,CAAC,QAAQ,EAAE,EAAE,QAAQ,IAAI,IAAI,CAAC;IACjE,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CACb,kCAAkC,QAAQ,cAAc,MAAM,KAAK;YACjE,qCAAqC,CACxC,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,SAAgB,iBAAiB,CAAI,QAAgB,EAAE,EAAW;IAChE,OAAO,6CAAoB,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,cAAc,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;AAC3E,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * TypeORM 통합 모듈.
3
+ *
4
+ * v0.1: TenantSubscriber + raw SQL 헬퍼
5
+ * v0.2: Prisma 어댑터 추가 예정 (별도 폴더)
6
+ * v0.3: Mongoose 어댑터
7
+ */
8
+ export * from './tenant.subscriber';
9
+ export * from './raw-sql.helper';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/typeorm/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC"}
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ /**
18
+ * TypeORM 통합 모듈.
19
+ *
20
+ * v0.1: TenantSubscriber + raw SQL 헬퍼
21
+ * v0.2: Prisma 어댑터 추가 예정 (별도 폴더)
22
+ * v0.3: Mongoose 어댑터
23
+ */
24
+ __exportStar(require("./tenant.subscriber"), exports);
25
+ __exportStar(require("./raw-sql.helper"), exports);
26
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/typeorm/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA;;;;;;GAMG;AACH,sDAAoC;AACpC,mDAAiC"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * ─────────────────────────────────────────────────────────────
3
+ * withTenantWhere — Raw SQL 쿼리에 안전하게 tenant 조건을 부착하는 헬퍼.
4
+ *
5
+ * TenantSubscriber는 ORM(Repository, QueryBuilder)을 통한 호출만 보호합니다.
6
+ * 사용자가 어쩔 수 없이 repository.query()로 raw SQL을 직접 실행할 때는
7
+ * 라이브러리가 자동 주입을 해줄 수 없으므로, 이 헬퍼로 명시적으로 감싸세요.
8
+ *
9
+ * ─────────────────────────────────────────────────────────────
10
+ *
11
+ * [사용 예시]
12
+ *
13
+ * // 위험: tenant 조건이 빠져있음 (보호 X)
14
+ * await repo.query('SELECT * FROM students');
15
+ *
16
+ * // 권장: 헬퍼가 자동으로 WHERE 추가
17
+ * const sql = withTenantWhere('SELECT * FROM students', 'tenant_id');
18
+ * await repo.query(sql.text, sql.params);
19
+ *
20
+ * ─────────────────────────────────────────────────────────────
21
+ *
22
+ * [반환 구조]
23
+ * {
24
+ * text: 'SELECT * FROM students WHERE tenant_id = $1',
25
+ * params: ['academy-A'],
26
+ * }
27
+ *
28
+ * SQL injection 방지를 위해 반드시 parameterized query로 반환합니다.
29
+ */
30
+ export interface WithTenantWhereResult {
31
+ text: string;
32
+ params: unknown[];
33
+ }
34
+ export declare function withTenantWhere(sql: string, tenantColumn: string, existingParams?: unknown[]): WithTenantWhereResult;
35
+ //# sourceMappingURL=raw-sql.helper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"raw-sql.helper.d.ts","sourceRoot":"","sources":["../../src/typeorm/raw-sql.helper.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,EAAE,CAAC;CACnB;AAED,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,MAAM,EACpB,cAAc,GAAE,OAAO,EAAO,GAC7B,qBAAqB,CAuBvB"}
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.withTenantWhere = withTenantWhere;
4
+ const get_current_tenant_id_1 = require("../context/get-current-tenant-id");
5
+ const missing_tenant_context_error_1 = require("../errors/missing-tenant-context.error");
6
+ function withTenantWhere(sql, tenantColumn, existingParams = []) {
7
+ const tenantId = (0, get_current_tenant_id_1.getCurrentTenantId)();
8
+ if (!tenantId) {
9
+ throw new missing_tenant_context_error_1.MissingTenantContextError('withTenantWhere: 현재 tenant 컨텍스트가 없습니다. raw SQL 사용 전 컨텍스트를 설정하세요.', 'raw-sql');
10
+ }
11
+ // SQL에 이미 WHERE가 있는지 단순 검사.
12
+ // (정확한 SQL 파서는 v0.2에서 도입 검토)
13
+ const hasWhere = /\bWHERE\b/i.test(sql);
14
+ const placeholder = `$${existingParams.length + 1}`;
15
+ const clause = `${tenantColumn} = ${placeholder}`;
16
+ const text = hasWhere
17
+ ? `${sql} AND ${clause}`
18
+ : `${sql} WHERE ${clause}`;
19
+ return {
20
+ text,
21
+ params: [...existingParams, tenantId],
22
+ };
23
+ }
24
+ //# sourceMappingURL=raw-sql.helper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"raw-sql.helper.js","sourceRoot":"","sources":["../../src/typeorm/raw-sql.helper.ts"],"names":[],"mappings":";;AAqCA,0CA2BC;AAhED,4EAAsE;AACtE,yFAAmF;AAoCnF,SAAgB,eAAe,CAC7B,GAAW,EACX,YAAoB,EACpB,iBAA4B,EAAE;IAE9B,MAAM,QAAQ,GAAG,IAAA,0CAAkB,GAAE,CAAC;IACtC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,wDAAyB,CACjC,kEAAkE,EAClE,SAAS,CACV,CAAC;IACJ,CAAC;IAED,4BAA4B;IAC5B,6BAA6B;IAC7B,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACxC,MAAM,WAAW,GAAG,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;IACpD,MAAM,MAAM,GAAG,GAAG,YAAY,MAAM,WAAW,EAAE,CAAC;IAElD,MAAM,IAAI,GAAG,QAAQ;QACnB,CAAC,CAAC,GAAG,GAAG,QAAQ,MAAM,EAAE;QACxB,CAAC,CAAC,GAAG,GAAG,UAAU,MAAM,EAAE,CAAC;IAE7B,OAAO;QACL,IAAI;QACJ,MAAM,EAAE,CAAC,GAAG,cAAc,EAAE,QAAQ,CAAC;KACtC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,61 @@
1
+ import { EntitySubscriberInterface, InsertEvent, LoadEvent } from 'typeorm';
2
+ import { TenantShieldOptions } from '../interfaces/tenant-shield-options.interface';
3
+ /**
4
+ * ─────────────────────────────────────────────────────────────
5
+ * TenantSubscriber — v0.1의 가장 중요한 컴포넌트.
6
+ *
7
+ * TypeORM의 EventSubscriber는 모든 SELECT/INSERT/UPDATE/DELETE
8
+ * 동작에 끼어들 수 있는 hook입니다. 이 hook을 활용해서 사용자가
9
+ * 코드에서 WHERE를 빠뜨려도, 라이브러리가 자동으로 안전망을 깔아줍니다.
10
+ *
11
+ * 적용되는 보호 3가지:
12
+ * 1) beforeQuery (또는 QueryBuilder 가로채기): SELECT/UPDATE/DELETE에 자동 WHERE 주입
13
+ * 2) beforeInsert: INSERT 시 현재 tenant ID를 자동으로 컬럼에 주입
14
+ * 3) afterLoad: 어떤 escape hatch로든 다른 tenant의 entity가 반환되면 즉시 throw
15
+ *
16
+ * ⚠️ Raw SQL (repository.query()) 은 이 hook을 통과하지 않습니다.
17
+ * 이 경우 사용자가 withTenantWhere() 헬퍼로 수동 보호해야 합니다.
18
+ * ─────────────────────────────────────────────────────────────
19
+ */
20
+ export declare class TenantSubscriber implements EntitySubscriberInterface {
21
+ private readonly options;
22
+ constructor(options: TenantShieldOptions);
23
+ /**
24
+ * INSERT 직전 hook.
25
+ * tenant_id 컬럼이 비어 있으면 현재 컨텍스트의 tenantId를 자동으로 채워 넣습니다.
26
+ *
27
+ * 사용자가 "tenantId를 명시적으로 안 적어도 알아서 들어가는" 마법은 여기서 발생.
28
+ */
29
+ beforeInsert(event: InsertEvent<any>): void;
30
+ /**
31
+ * Entity가 DB에서 로드된 직후 hook.
32
+ *
33
+ * 어떤 경로로든 (raw SQL, escape hatch, 우회 코드) 다른 tenant의 데이터가
34
+ * 메모리에 올라왔다면 여기서 마지막으로 검출해 throw.
35
+ *
36
+ * "다층 방어"의 최종 안전망입니다.
37
+ */
38
+ afterLoad(entity: any, event?: LoadEvent<any>): void;
39
+ /**
40
+ * QueryBuilder의 SELECT/UPDATE/DELETE에 자동 WHERE 주입.
41
+ *
42
+ * TypeORM 0.3+에서는 beforeQuery 같은 hook이 제한적이라,
43
+ * 실제 구현은 EntityManager wrap 또는 QueryBuilder 가로채기를 통해 이뤄집니다.
44
+ *
45
+ * v0.1에서는 QueryBuilder 프로토타입을 monkey-patch하여
46
+ * getMany/getOne/getCount/execute 전에 자동 조건을 주입합니다.
47
+ */
48
+ /**
49
+ * 주어진 entity 클래스가 tenant 보호 대상인지 판정.
50
+ *
51
+ * 사용자가 forRoot에 entities를 명시했으면 그 화이트리스트로만 확인.
52
+ * 미명시 시 모든 entity를 검사 (기본 안전 모드).
53
+ */
54
+ private isTenantAwareEntity;
55
+ }
56
+ /**
57
+ * 모든 prototype 패치를 원복하고 모듈 상태를 초기화한다.
58
+ * e2e 테스트의 afterAll에서 호출해 테스트 간 오염을 방지한다.
59
+ */
60
+ export declare function unpatch(): void;
61
+ //# sourceMappingURL=tenant.subscriber.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenant.subscriber.d.ts","sourceRoot":"","sources":["../../src/typeorm/tenant.subscriber.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,yBAAyB,EAEzB,WAAW,EACX,SAAS,EAIV,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,mBAAmB,EAAE,MAAM,+CAA+C,CAAC;AAIpF;;;;;;;;;;;;;;;;GAgBG;AACH,qBACa,gBAAiB,YAAW,yBAAyB;IACpD,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,mBAAmB;IAOzD;;;;;OAKG;IACH,YAAY,CAAC,KAAK,EAAE,WAAW,CAAC,GAAG,CAAC,GAAG,IAAI;IAwC3C;;;;;;;OAOG;IACH,SAAS,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,CAAC,EAAE,SAAS,CAAC,GAAG,CAAC,GAAG,IAAI;IAwBpD;;;;;;;;OAQG;IAGH;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB;CAQ5B;AAiWD;;;GAGG;AACH,wBAAgB,OAAO,IAAI,IAAI,CAoB9B"}