@breadstone/archipel-mcp 0.0.40 → 0.0.42

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 (131) hide show
  1. package/data/guides/blob-storage.md +20 -16
  2. package/data/guides/implementing-ports.md +2 -0
  3. package/data/guides/payments-and-feature-gating.md +76 -45
  4. package/data/packages/platform-blob-storage/api/Class.AwsS3BlobStorageStrategy.md +197 -0
  5. package/data/packages/platform-blob-storage/api/Class.AzureBlobStorageStrategy.md +197 -0
  6. package/data/packages/platform-blob-storage/api/Class.BlobModule.md +20 -10
  7. package/data/packages/platform-blob-storage/api/Class.BlobService.md +8 -8
  8. package/data/packages/platform-blob-storage/api/Class.BlobStorageStrategyBase.md +158 -0
  9. package/data/packages/platform-blob-storage/api/Class.LocalBlobStorageStrategy.md +197 -0
  10. package/data/packages/platform-blob-storage/api/Class.VercelBlobStorageStrategy.md +198 -0
  11. package/data/packages/platform-blob-storage/api/Interface.IBlobDeleteRequest.md +4 -4
  12. package/data/packages/platform-blob-storage/api/Interface.IBlobDownloadRequest.md +5 -5
  13. package/data/packages/platform-blob-storage/api/Interface.IBlobDownloadResult.md +6 -6
  14. package/data/packages/platform-blob-storage/api/Interface.IBlobModuleOptions.md +20 -10
  15. package/data/packages/platform-blob-storage/api/Interface.IBlobObjectMetadata.md +14 -14
  16. package/data/packages/platform-blob-storage/api/Interface.IBlobSignedUrlRequest.md +5 -5
  17. package/data/packages/platform-blob-storage/api/Interface.IBlobUploadRequest.md +10 -10
  18. package/data/packages/platform-blob-storage/api/Interface.IBlobUploadResult.md +3 -3
  19. package/data/packages/platform-blob-storage/api/TypeAlias.BlobDownloadResponseType.md +1 -1
  20. package/data/packages/platform-blob-storage/api/TypeAlias.BlobUploadBody.md +1 -1
  21. package/data/packages/platform-blob-storage/api/Variable.AWS_S3_ACCESS_KEY_ID.md +14 -0
  22. package/data/packages/platform-blob-storage/api/Variable.AWS_S3_BUCKET.md +14 -0
  23. package/data/packages/platform-blob-storage/api/Variable.AWS_S3_CONFIG_ENTRIES.md +14 -0
  24. package/data/packages/platform-blob-storage/api/Variable.AWS_S3_ENDPOINT.md +14 -0
  25. package/data/packages/platform-blob-storage/api/Variable.AWS_S3_REGION.md +14 -0
  26. package/data/packages/platform-blob-storage/api/Variable.AWS_S3_SECRET_ACCESS_KEY.md +14 -0
  27. package/data/packages/platform-blob-storage/api/Variable.AZURE_BLOB_CONNECTION_STRING.md +14 -0
  28. package/data/packages/platform-blob-storage/api/Variable.AZURE_BLOB_CONTAINER_NAME.md +14 -0
  29. package/data/packages/platform-blob-storage/api/Variable.AZURE_CONFIG_ENTRIES.md +14 -0
  30. package/data/packages/platform-blob-storage/api/{Variable.BLOB_PROVIDER.md → Variable.BLOB_STORAGE_STRATEGY_TOKEN.md} +3 -3
  31. package/data/packages/platform-blob-storage/api/Variable.BlobDownloadResponseTypes.md +5 -5
  32. package/data/packages/platform-blob-storage/api/Variable.LOCAL_BLOB_BASE_PATH.md +14 -0
  33. package/data/packages/platform-blob-storage/api/Variable.LOCAL_BLOB_BUCKET.md +14 -0
  34. package/data/packages/platform-blob-storage/api/Variable.LOCAL_CONFIG_ENTRIES.md +14 -0
  35. package/data/packages/platform-blob-storage/api/Variable.PLATFORM_BLOB_STORAGE_CONFIG_ENTRIES.md +14 -0
  36. package/data/packages/platform-blob-storage/api/Variable.VERCEL_BLOB_BUCKET.md +14 -0
  37. package/data/packages/platform-blob-storage/api/Variable.VERCEL_BLOB_PUBLIC_URL.md +14 -0
  38. package/data/packages/platform-blob-storage/api/Variable.VERCEL_BLOB_READ_WRITE_TOKEN.md +14 -0
  39. package/data/packages/platform-blob-storage/api/Variable.VERCEL_BLOB_REGION.md +14 -0
  40. package/data/packages/platform-blob-storage/api/Variable.VERCEL_BLOB_URL.md +14 -0
  41. package/data/packages/platform-blob-storage/api/Variable.VERCEL_CONFIG_ENTRIES.md +14 -0
  42. package/data/packages/platform-blob-storage/api/index.md +26 -18
  43. package/data/packages/platform-blob-storage/index.md +104 -68
  44. package/data/packages/platform-bootstrap/api/index.md +0 -2
  45. package/data/packages/platform-core/api/Class.ProblemDetailResponse.md +8 -8
  46. package/data/packages/platform-database/api/Class.DatabaseHealthIndicator.md +4 -4
  47. package/data/packages/platform-database/api/Class.DatabaseModule.md +3 -3
  48. package/data/packages/platform-database/api/Class.DatabaseService.md +12 -11
  49. package/data/packages/platform-database/api/Class.PrismaService.md +6 -5
  50. package/data/packages/platform-database/api/Class.RepositoryBase.md +23 -23
  51. package/data/packages/platform-database/api/Class.RepositoryBase2.md +7 -7
  52. package/data/packages/platform-database/api/Class.RepositoryExceptionFilter.md +2 -2
  53. package/data/packages/platform-database/api/Function.aggregate.md +1 -1
  54. package/data/packages/platform-database/api/Function.aggregateRaw.md +1 -1
  55. package/data/packages/platform-database/api/Function.count.md +1 -1
  56. package/data/packages/platform-database/api/Function.create.md +1 -1
  57. package/data/packages/platform-database/api/Function.createMany.md +1 -1
  58. package/data/packages/platform-database/api/Function.createManyAndReturn.md +1 -1
  59. package/data/packages/platform-database/api/Function.cursorPaginator.md +1 -1
  60. package/data/packages/platform-database/api/Function.findFirst.md +1 -1
  61. package/data/packages/platform-database/api/Function.findFirstOrThrow.md +1 -1
  62. package/data/packages/platform-database/api/Function.findMany.md +1 -1
  63. package/data/packages/platform-database/api/Function.findManyPaged.md +1 -1
  64. package/data/packages/platform-database/api/Function.findRaw.md +1 -1
  65. package/data/packages/platform-database/api/Function.findUnique.md +1 -1
  66. package/data/packages/platform-database/api/Function.findUniqueOrThrow.md +1 -1
  67. package/data/packages/platform-database/api/Function.groupBy.md +1 -1
  68. package/data/packages/platform-database/api/Function.pageable.md +1 -1
  69. package/data/packages/platform-database/api/Function.paginator.md +1 -1
  70. package/data/packages/platform-database/api/Function.pipe.md +1 -1
  71. package/data/packages/platform-database/api/Function.pipeAll.md +1 -1
  72. package/data/packages/platform-database/api/Function.query.md +1 -1
  73. package/data/packages/platform-database/api/Function.query2.md +1 -1
  74. package/data/packages/platform-database/api/Function.remove.md +1 -1
  75. package/data/packages/platform-database/api/Function.removeMany.md +1 -1
  76. package/data/packages/platform-database/api/Function.skip.md +1 -1
  77. package/data/packages/platform-database/api/Function.transactionalQuery.md +1 -1
  78. package/data/packages/platform-database/api/Function.update.md +1 -1
  79. package/data/packages/platform-database/api/Function.updateMany.md +1 -1
  80. package/data/packages/platform-database/api/Function.updateManyAndReturn.md +1 -1
  81. package/data/packages/platform-database/api/Function.upsert.md +1 -1
  82. package/data/packages/platform-database/api/Interface.ICursorPaginateOptions.md +4 -4
  83. package/data/packages/platform-database/api/Interface.ICursorPaginatedResult.md +3 -3
  84. package/data/packages/platform-database/api/Interface.IDatabaseModuleConfig.md +3 -3
  85. package/data/packages/platform-database/api/Interface.IPaginateOptions.md +3 -3
  86. package/data/packages/platform-database/api/Interface.IPaginatedResult.md +3 -3
  87. package/data/packages/platform-database/api/Interface.IPrismaServiceOptions.md +2 -2
  88. package/data/packages/platform-database/api/Interface.IRepositoryQuery.md +3 -3
  89. package/data/packages/platform-database/api/Interface.IRepositoryQuery2.md +3 -3
  90. package/data/packages/platform-database/api/Interface.ITransactionalRepositoryQuery.md +4 -4
  91. package/data/packages/platform-database/api/TypeAlias.CursorPaginateFunction.md +1 -1
  92. package/data/packages/platform-database/api/TypeAlias.DelegateArgs.md +1 -1
  93. package/data/packages/platform-database/api/TypeAlias.DelegateOperations.md +1 -1
  94. package/data/packages/platform-database/api/TypeAlias.DelegateReturnTypes.md +1 -1
  95. package/data/packages/platform-database/api/TypeAlias.PaginateFunction.md +1 -1
  96. package/data/packages/platform-database/api/TypeAlias.QueryResultType.md +1 -1
  97. package/data/packages/platform-database/api/TypeAlias.RepositoryOperationsAll.md +1 -1
  98. package/data/packages/platform-database/api/TypeAlias.RepositoryOperationsBase.md +1 -1
  99. package/data/packages/platform-database/api/TypeAlias.TransactionalQueryResultType.md +1 -1
  100. package/data/packages/platform-database/api/Variable.DATABASE_MODULE_CONFIG.md +1 -1
  101. package/data/packages/platform-database/api/Variable.DB_AUTO_CONNECTION.md +1 -1
  102. package/data/packages/platform-database/api/Variable.PLATFORM_DATABASE_CONFIG_ENTRIES.md +1 -1
  103. package/data/packages/platform-payments/api/Class.FeatureAccessPort.md +13 -5
  104. package/data/packages/platform-payments/api/Class.FeatureGuard.md +3 -3
  105. package/data/packages/platform-payments/api/Class.FeatureUsageInterceptor.md +3 -3
  106. package/data/packages/platform-payments/api/Function.RequiresFeature.md +1 -1
  107. package/data/packages/platform-payments/api/Interface.IFeatureAccessContext.md +46 -0
  108. package/data/packages/platform-payments/api/Variable.FEATURE_KEY_METADATA.md +1 -1
  109. package/data/packages/platform-payments/api/index.md +1 -0
  110. package/data/packages/platform-payments/index.md +33 -12
  111. package/data/patterns/guard-pattern.md +27 -6
  112. package/data/patterns/module-pattern.md +6 -2
  113. package/package.json +1 -1
  114. package/data/packages/platform-blob-storage/api/Class.AwsS3BlobProvider.md +0 -196
  115. package/data/packages/platform-blob-storage/api/Class.AzureBlobProvider.md +0 -196
  116. package/data/packages/platform-blob-storage/api/Class.VercelBlobProvider.md +0 -197
  117. package/data/packages/platform-blob-storage/api/Interface.IAwsS3BlobProviderRegistration.md +0 -28
  118. package/data/packages/platform-blob-storage/api/Interface.IAwsS3ProviderOptions.md +0 -68
  119. package/data/packages/platform-blob-storage/api/Interface.IAzureBlobProviderOptions.md +0 -48
  120. package/data/packages/platform-blob-storage/api/Interface.IAzureBlobProviderRegistration.md +0 -28
  121. package/data/packages/platform-blob-storage/api/Interface.IBlobProvider.md +0 -114
  122. package/data/packages/platform-blob-storage/api/Interface.ICustomBlobProviderRegistration.md +0 -28
  123. package/data/packages/platform-blob-storage/api/Interface.IVercelBlobProviderOptions.md +0 -80
  124. package/data/packages/platform-blob-storage/api/Interface.IVercelBlobProviderRegistration.md +0 -28
  125. package/data/packages/platform-blob-storage/api/TypeAlias.IBlobProviderRegistration.md +0 -16
  126. package/data/packages/platform-blob-storage/api/Variable.AWS_S3_PROVIDER_OPTIONS.md +0 -12
  127. package/data/packages/platform-blob-storage/api/Variable.AZURE_BLOB_PROVIDER_OPTIONS.md +0 -12
  128. package/data/packages/platform-blob-storage/api/Variable.BlobProviderKinds.md +0 -26
  129. package/data/packages/platform-blob-storage/api/Variable.VERCEL_BLOB_PROVIDER_OPTIONS.md +0 -12
  130. package/data/packages/platform-bootstrap/api/Function.useExpressServer.md +0 -32
  131. package/data/packages/platform-bootstrap/api/Interface.IExpressServerStepOptions.md +0 -46
@@ -18,16 +18,17 @@ yarn add @breadstone/archipel-platform-blob-storage
18
18
 
19
19
  ---
20
20
 
21
- ## Choose a Provider
21
+ ## Choose a Strategy
22
22
 
23
- `platform-blob-storage` abstracts file operations behind a unified `BlobService`. You select the provider at module registration time. Supported providers:
23
+ `platform-blob-storage` abstracts file operations behind a unified `BlobService`. You select the storage strategy at module registration time. Built-in strategies:
24
24
 
25
- | Provider | Kind constant | When to use |
26
- | ---------- | -------------------------- | ------------------------------------------- |
27
- | **Vercel** | `BlobProviderKinds.Vercel` | Vercel-hosted apps with Vercel Blob storage |
28
- | **Azure** | `BlobProviderKinds.Azure` | Azure Blob Storage accounts |
29
- | **AWS S3** | `BlobProviderKinds.AwsS3` | Amazon S3 or S3-compatible providers |
30
- | **Custom** | `BlobProviderKinds.Custom` | Your own storage backend |
25
+ | Strategy | Import subpath | When to use |
26
+ | ----------------------------- | --------------------- | ------------------------------------------- |
27
+ | **Vercel** | `strategies/vercel` | Vercel-hosted apps with Vercel Blob storage |
28
+ | **Azure** | `strategies/azure` | Azure Blob Storage accounts |
29
+ | **AWS S3** | `strategies/aws-s3` | Amazon S3 or S3-compatible providers |
30
+ | **Local** | `strategies/local` | Local filesystem (development / testing) |
31
+ | **Custom** | — | Extend `BlobStorageStrategyBase` |
31
32
 
32
33
  ---
33
34
 
@@ -35,14 +36,14 @@ yarn add @breadstone/archipel-platform-blob-storage
35
36
 
36
37
  ```typescript
37
38
  import { Module } from '@nestjs/common';
38
- import { BlobModule, BlobProviderKinds } from '@breadstone/archipel-platform-blob-storage';
39
+ import { BlobModule } from '@breadstone/archipel-platform-blob-storage';
40
+ import { AwsS3BlobStorageStrategy, AWS_S3_CONFIG_ENTRIES } from '@breadstone/archipel-platform-blob-storage/strategies/aws-s3';
39
41
 
40
42
  @Module({
41
43
  imports: [
42
- BlobModule.forRoot({
43
- provider: {
44
- kind: BlobProviderKinds.AwsS3,
45
- },
44
+ BlobModule.register({
45
+ storageStrategy: AwsS3BlobStorageStrategy,
46
+ configEntries: AWS_S3_CONFIG_ENTRIES,
46
47
  }),
47
48
  ],
48
49
  })
@@ -234,8 +235,11 @@ export class PrismaBlobVariantAdapter extends BlobVariantPersistencePort {
234
235
  ### Register with Persistence
235
236
 
236
237
  ```typescript
237
- BlobModule.forRoot({
238
- provider: { kind: BlobProviderKinds.AwsS3 },
238
+ import { AwsS3BlobStorageStrategy, AWS_S3_CONFIG_ENTRIES } from '@breadstone/archipel-platform-blob-storage/strategies/aws-s3';
239
+
240
+ BlobModule.register({
241
+ storageStrategy: AwsS3BlobStorageStrategy,
242
+ configEntries: AWS_S3_CONFIG_ENTRIES,
239
243
  objectPersistence: PrismaBlobObjectAdapter,
240
244
  variantPersistence: PrismaBlobVariantAdapter,
241
245
  }),
@@ -245,7 +249,7 @@ BlobModule.forRoot({
245
249
 
246
250
  ## Security
247
251
 
248
- All built-in blob providers (AWS S3, Azure, Vercel) apply automatic key normalization via the internal `normalizeKey` method before any storage operation. This includes:
252
+ All built-in blob storage strategies (AWS S3, Azure, Vercel, Local) apply automatic key normalization via the internal `normalizeKey` method before any storage operation. This includes:
249
253
 
250
254
  - **Path traversal prevention** — segments such as `..` and `.` are removed so callers cannot escape the intended directory.
251
255
  - **Backslash normalization** — backslashes (`\`) are converted to forward slashes before processing.
@@ -304,6 +304,8 @@ When omitted, blob uploads and deletes work normally, but metadata isn't tracked
304
304
 
305
305
  When omitted, `FeatureGuard` and `FeatureUsageInterceptor` have no access port and will deny or skip feature checks. When provided, they delegate quota enforcement and usage recording to your adapter.
306
306
 
307
+ `FeatureGuard` calls `checkAccess(userId, featureKey, context)`, while `FeatureUsageInterceptor` calls `recordUsage(userId, featureKey, context)` after a successful request. The `IFeatureAccessContext` contains the resolved `userId`, optional `tenantId`, and original authenticated subject, so adapters can enforce tenant-scoped plans without controller-specific billing logic.
308
+
307
309
  ---
308
310
 
309
311
  ## Common Mistakes
@@ -36,16 +36,15 @@ yarn add @breadstone/archipel-platform-payments
36
36
  ```typescript
37
37
  import { Module } from '@nestjs/common';
38
38
  import { PaymentModule } from '@breadstone/archipel-platform-payments';
39
- import { StripePaymentClient } from './adapters/StripePaymentClient';
39
+ import { StripeClient, STRIPE_CONFIG_ENTRIES } from '@breadstone/archipel-platform-payments/stripe';
40
40
  import { PrismaFeatureAccessAdapter } from './adapters/PrismaFeatureAccessAdapter';
41
- import { STRIPE_CONFIG_ENTRIES } from './env';
42
41
 
43
42
  @Module({
44
43
  imports: [
45
44
  PaymentModule.register({
46
- paymentClient: StripePaymentClient,
45
+ paymentClient: StripeClient,
47
46
  featureAccess: PrismaFeatureAccessAdapter,
48
- configEntries: [...STRIPE_CONFIG_ENTRIES],
47
+ configEntries: STRIPE_CONFIG_ENTRIES,
49
48
  }),
50
49
  ],
51
50
  })
@@ -54,9 +53,9 @@ export class AppModule {}
54
53
 
55
54
  ---
56
55
 
57
- ## Implementing the Payment Client Port
56
+ ## Using the Payment Client Port
58
57
 
59
- The `PaymentClientPort` defines the contract between the library and your payment provider. Implement it for your chosen provider:
58
+ The built-in provider clients implement `PaymentClientPort`. Inject the port in application services so checkout and webhook code stays provider-agnostic:
60
59
 
61
60
  ```typescript
62
61
  import { Injectable } from '@nestjs/common';
@@ -64,23 +63,27 @@ import {
64
63
  PaymentClientPort,
65
64
  type INormalizedCheckoutSession,
66
65
  type INormalizedSubscription,
67
- type INormalizedWebhookEvent,
68
66
  } from '@breadstone/archipel-platform-payments';
69
67
 
70
68
  @Injectable()
71
- export class StripePaymentClient extends PaymentClientPort {
72
- public async createCheckoutSession(params: Record<string, unknown>): Promise<INormalizedCheckoutSession> {
73
- // Use Stripe SDK to create a checkout session
74
- // Return a normalized result
69
+ export class CheckoutService {
70
+ private readonly _paymentClient: PaymentClientPort;
71
+
72
+ public constructor(paymentClient: PaymentClientPort) {
73
+ this._paymentClient = paymentClient;
75
74
  }
76
75
 
77
- public async getSubscription(subscriptionId: string): Promise<INormalizedSubscription | null> {
78
- // Fetch subscription details from Stripe
79
- // Return normalized subscription data
76
+ public async createCheckout(priceId: string, userId: string): Promise<INormalizedCheckoutSession> {
77
+ return this._paymentClient.createCheckoutSession(
78
+ priceId,
79
+ 'https://app.example.com/billing/success',
80
+ 'https://app.example.com/billing/cancel',
81
+ userId,
82
+ );
80
83
  }
81
84
 
82
- public async handleWebhook(payload: Buffer, signature: string): Promise<INormalizedWebhookEvent> {
83
- // Verify webhook signature and normalize the event
85
+ public async getSubscription(subscriptionId: string): Promise<INormalizedSubscription | null> {
86
+ return this._paymentClient.fetchSubscription(subscriptionId);
84
87
  }
85
88
  }
86
89
  ```
@@ -97,7 +100,11 @@ The `FeatureAccessPort` lets you restrict functionality based on subscription ti
97
100
 
98
101
  ```typescript
99
102
  import { Injectable } from '@nestjs/common';
100
- import { FeatureAccessPort } from '@breadstone/archipel-platform-payments';
103
+ import {
104
+ FeatureAccessPort,
105
+ type IFeatureAccessContext,
106
+ type IFeatureAccessResult,
107
+ } from '@breadstone/archipel-platform-payments';
101
108
  import { PrismaService } from '@breadstone/archipel-platform-database';
102
109
 
103
110
  @Injectable()
@@ -109,53 +116,77 @@ export class PrismaFeatureAccessAdapter extends FeatureAccessPort {
109
116
  this._prisma = prisma;
110
117
  }
111
118
 
112
- public async hasAccess(userId: string, feature: string): Promise<boolean> {
119
+ public async checkAccess(
120
+ userId: string,
121
+ featureKey: string,
122
+ context?: IFeatureAccessContext,
123
+ ): Promise<IFeatureAccessResult> {
113
124
  const subscription = await this._prisma.subscription.findFirst({
114
- where: { userId, status: 'active' },
115
- include: { plan: true },
125
+ where: { userId, tenantId: context?.tenantId, status: 'active' },
126
+ include: { plan: { include: { features: true } } },
116
127
  });
117
128
 
118
- if (!subscription) {
119
- return false;
129
+ const feature = subscription?.plan.features.find((planFeature) => planFeature.key === featureKey);
130
+ if (!feature) {
131
+ return { allowed: false, used: 0, limit: 0, remaining: 0, resetAt: new Date() };
120
132
  }
121
133
 
122
- return subscription.plan.features.includes(feature);
123
- }
124
-
125
- public async recordUsage(userId: string, feature: string): Promise<void> {
126
- await this._prisma.featureUsage.create({
127
- data: { userId, feature, usedAt: new Date() },
134
+ const used = await this._prisma.featureUsage.count({
135
+ where: {
136
+ userId,
137
+ tenantId: context?.tenantId,
138
+ featureKey,
139
+ usedAt: { gte: subscription.currentPeriodStart },
140
+ },
128
141
  });
142
+
143
+ const limit = feature.limit;
144
+ const remaining = limit === -1 ? -1 : Math.max(0, limit - used);
145
+
146
+ return {
147
+ allowed: limit === -1 || used < limit,
148
+ used,
149
+ limit,
150
+ remaining,
151
+ resetAt: subscription.currentPeriodEnd,
152
+ };
129
153
  }
130
154
 
131
- public async getUsageCount(userId: string, feature: string, since: Date): Promise<number> {
132
- return this._prisma.featureUsage.count({
133
- where: { userId, feature, usedAt: { gte: since } },
155
+ public async recordUsage(userId: string, featureKey: string, context?: IFeatureAccessContext): Promise<void> {
156
+ const access = await this.checkAccess(userId, featureKey, context);
157
+ if (!access.allowed) {
158
+ throw new Error(`Feature quota exceeded for ${featureKey}`);
159
+ }
160
+
161
+ await this._prisma.featureUsage.create({
162
+ data: { userId, tenantId: context?.tenantId, featureKey, usedAt: new Date() },
134
163
  });
135
164
  }
136
165
  }
137
166
  ```
138
167
 
168
+ `FeatureGuard` and `FeatureUsageInterceptor` pass `IFeatureAccessContext` into this adapter. The context includes the resolved `userId`, an optional `tenantId` from `request.user.tenantId`, and the original authenticated subject.
169
+
139
170
  ### Step 2: Use the Feature Guard
140
171
 
141
- The `FeatureGuard` decorator restricts endpoints to users with access to specific features:
172
+ Use `@RequiresFeature()` to mark endpoints and `FeatureGuard` to enforce access checks:
142
173
 
143
174
  ```typescript
144
175
  import { Controller, Post, UseGuards } from '@nestjs/common';
145
176
  import { JwtAuthGuard } from '@breadstone/archipel-platform-authentication';
146
- import { FeatureGuard } from '@breadstone/archipel-platform-payments';
177
+ import { FeatureGuard, RequiresFeature } from '@breadstone/archipel-platform-payments';
147
178
 
148
179
  @Controller('exports')
149
- @UseGuards(JwtAuthGuard)
180
+ @UseGuards(JwtAuthGuard, FeatureGuard)
150
181
  export class ExportController {
151
182
  @Post('pdf')
152
- @FeatureGuard('export:pdf')
183
+ @RequiresFeature('export:pdf')
153
184
  public async exportPdf(): Promise<void> {
154
185
  // Only accessible to users with 'export:pdf' feature
155
186
  }
156
187
 
157
188
  @Post('csv')
158
- @FeatureGuard('export:csv')
189
+ @RequiresFeature('export:csv')
159
190
  public async exportCsv(): Promise<void> {
160
191
  // Only accessible to users with 'export:csv' feature
161
192
  }
@@ -169,14 +200,14 @@ export class ExportController {
169
200
  ```typescript
170
201
  import { Controller, Post, UseGuards, UseInterceptors } from '@nestjs/common';
171
202
  import { JwtAuthGuard } from '@breadstone/archipel-platform-authentication';
172
- import { FeatureGuard, FeatureUsageInterceptor } from '@breadstone/archipel-platform-payments';
203
+ import { FeatureGuard, FeatureUsageInterceptor, RequiresFeature } from '@breadstone/archipel-platform-payments';
173
204
 
174
205
  @Controller('ai')
175
- @UseGuards(JwtAuthGuard)
206
+ @UseGuards(JwtAuthGuard, FeatureGuard)
207
+ @UseInterceptors(FeatureUsageInterceptor)
176
208
  export class AiController {
177
209
  @Post('generate')
178
- @FeatureGuard('ai:generate')
179
- @UseInterceptors(FeatureUsageInterceptor)
210
+ @RequiresFeature('ai:generate')
180
211
  public async generate(): Promise<string> {
181
212
  // Usage is recorded automatically after success
182
213
  return 'generated content';
@@ -208,17 +239,16 @@ export class PaymentWebhookController {
208
239
  @Req() request: RawBodyRequest<Request>,
209
240
  @Headers('stripe-signature') signature: string,
210
241
  ): Promise<void> {
211
- const event = await this._paymentClient.handleWebhook(request.rawBody!, signature);
242
+ const event = await this._paymentClient.constructWebhookEvent(request.rawBody!, signature);
212
243
 
213
244
  switch (event.type) {
214
- case 'subscription.created':
215
245
  case 'subscription.updated':
216
246
  // Update subscription in your database
217
247
  break;
218
248
  case 'subscription.deleted':
219
249
  // Handle cancellation
220
250
  break;
221
- case 'payment.failed':
251
+ case 'invoice.payment_failed':
222
252
  // Notify user, retry logic
223
253
  break;
224
254
  }
@@ -234,7 +264,8 @@ export class PaymentWebhookController {
234
264
  2. **Verify webhook signatures.** Always validate the webhook payload before processing.
235
265
  3. **Use normalized models.** Don't leak provider-specific types into your business logic.
236
266
  4. **Implement idempotent webhook handlers.** Webhooks can be delivered multiple times.
237
- 5. **Separate feature gating from billing logic.** The `FeatureAccessPort` should only check access, not manage subscriptions.
267
+ 5. **Use access context for tenancy.** Read `context.tenantId` instead of coupling controllers to billing tables.
268
+ 6. **Separate feature gating from billing logic.** The `FeatureAccessPort` should check access and record metered usage, not manage subscriptions.
238
269
 
239
270
  ## Error Handling
240
271
 
@@ -258,7 +289,7 @@ All payment clients throw `PaymentError` — a domain error that wraps the under
258
289
  import { PaymentError } from '@breadstone/archipel-platform-payments';
259
290
 
260
291
  try {
261
- await this._paymentClient.createCheckoutSession(params);
292
+ await this._paymentClient.createCheckoutSession(priceId, successUrl, cancelUrl, userId);
262
293
  } catch (error) {
263
294
  if (error instanceof PaymentError) {
264
295
  // error.provider → 'stripe', 'paddle', 'mollie', 'lemonsqueezy'
@@ -0,0 +1,197 @@
1
+ ---
2
+ title: 'Class: AwsS3BlobStorageStrategy'
3
+ generated: true
4
+ editUrl: false
5
+ ---
6
+ # Class: AwsS3BlobStorageStrategy
7
+
8
+ Defined in: [strategies/aws-s3/AwsS3BlobStorageStrategy.ts:26](https://github.com/RueDeRennes/archipel/blob/main/libs/platform-blob-storage/src/strategies/aws-s3/AwsS3BlobStorageStrategy.ts#L26)
9
+
10
+ Provides a blob storage strategy backed by Amazon S3.
11
+
12
+ ## Extends
13
+
14
+ - [`BlobStorageStrategyBase`](Class.BlobStorageStrategyBase)
15
+
16
+ ## Constructors
17
+
18
+ ### Constructor
19
+
20
+ ```ts
21
+ new AwsS3BlobStorageStrategy(configService): AwsS3BlobStorageStrategy;
22
+ ```
23
+
24
+ Defined in: [strategies/aws-s3/AwsS3BlobStorageStrategy.ts:47](https://github.com/RueDeRennes/archipel/blob/main/libs/platform-blob-storage/src/strategies/aws-s3/AwsS3BlobStorageStrategy.ts#L47)
25
+
26
+ Initializes a new instance of the AwsS3BlobStorageStrategy class.
27
+
28
+ #### Parameters
29
+
30
+ | Parameter | Type | Description |
31
+ | ------ | ------ | ------ |
32
+ | `configService` | `ConfigService` | The configuration service providing environment values. |
33
+
34
+ #### Returns
35
+
36
+ `AwsS3BlobStorageStrategy`
37
+
38
+ #### Overrides
39
+
40
+ [`BlobStorageStrategyBase`](Class.BlobStorageStrategyBase).[`constructor`](Class.BlobStorageStrategyBase#constructor)
41
+
42
+ ## Properties
43
+
44
+ ### providerId
45
+
46
+ ```ts
47
+ readonly providerId: string = 'aws-s3';
48
+ ```
49
+
50
+ Defined in: [strategies/aws-s3/AwsS3BlobStorageStrategy.ts:77](https://github.com/RueDeRennes/archipel/blob/main/libs/platform-blob-storage/src/strategies/aws-s3/AwsS3BlobStorageStrategy.ts#L77)
51
+
52
+ Gets the unique identifier for this storage provider.
53
+
54
+ #### Overrides
55
+
56
+ [`BlobStorageStrategyBase`](Class.BlobStorageStrategyBase).[`providerId`](Class.BlobStorageStrategyBase#providerid)
57
+
58
+ ## Accessors
59
+
60
+ ### defaultBucket
61
+
62
+ #### Get Signature
63
+
64
+ ```ts
65
+ get defaultBucket(): string;
66
+ ```
67
+
68
+ Defined in: [strategies/aws-s3/AwsS3BlobStorageStrategy.ts:80](https://github.com/RueDeRennes/archipel/blob/main/libs/platform-blob-storage/src/strategies/aws-s3/AwsS3BlobStorageStrategy.ts#L80)
69
+
70
+ Gets the default bucket (container/directory) configured for the provider.
71
+
72
+ ##### Returns
73
+
74
+ `string`
75
+
76
+ Gets the default bucket (container/directory) configured for the provider.
77
+
78
+ #### Overrides
79
+
80
+ [`BlobStorageStrategyBase`](Class.BlobStorageStrategyBase).[`defaultBucket`](Class.BlobStorageStrategyBase#defaultbucket)
81
+
82
+ ## Methods
83
+
84
+ ### createSignedUrl()
85
+
86
+ ```ts
87
+ createSignedUrl(request): Promise<string>;
88
+ ```
89
+
90
+ Defined in: [strategies/aws-s3/AwsS3BlobStorageStrategy.ts:177](https://github.com/RueDeRennes/archipel/blob/main/libs/platform-blob-storage/src/strategies/aws-s3/AwsS3BlobStorageStrategy.ts#L177)
91
+
92
+ Creates a signed URL for the specified blob object.
93
+ Not all providers support this operation.
94
+
95
+ #### Parameters
96
+
97
+ | Parameter | Type | Description |
98
+ | ------ | ------ | ------ |
99
+ | `request` | [`IBlobSignedUrlRequest`](Interface.IBlobSignedUrlRequest) | The request describing the object to link. |
100
+
101
+ #### Returns
102
+
103
+ `Promise`\<`string`\>
104
+
105
+ The generated signed URL.
106
+
107
+ #### Overrides
108
+
109
+ [`BlobStorageStrategyBase`](Class.BlobStorageStrategyBase).[`createSignedUrl`](Class.BlobStorageStrategyBase#createsignedurl)
110
+
111
+ ***
112
+
113
+ ### deleteObject()
114
+
115
+ ```ts
116
+ deleteObject(request): Promise<void>;
117
+ ```
118
+
119
+ Defined in: [strategies/aws-s3/AwsS3BlobStorageStrategy.ts:159](https://github.com/RueDeRennes/archipel/blob/main/libs/platform-blob-storage/src/strategies/aws-s3/AwsS3BlobStorageStrategy.ts#L159)
120
+
121
+ Deletes a blob object from the storage backend.
122
+
123
+ #### Parameters
124
+
125
+ | Parameter | Type | Description |
126
+ | ------ | ------ | ------ |
127
+ | `request` | [`IBlobDeleteRequest`](Interface.IBlobDeleteRequest) | The delete request describing the object to remove. |
128
+
129
+ #### Returns
130
+
131
+ `Promise`\<`void`\>
132
+
133
+ #### Overrides
134
+
135
+ [`BlobStorageStrategyBase`](Class.BlobStorageStrategyBase).[`deleteObject`](Class.BlobStorageStrategyBase#deleteobject)
136
+
137
+ ***
138
+
139
+ ### downloadObject()
140
+
141
+ ```ts
142
+ downloadObject<TData>(request): Promise<IBlobDownloadResult<TData>>;
143
+ ```
144
+
145
+ Defined in: [strategies/aws-s3/AwsS3BlobStorageStrategy.ts:134](https://github.com/RueDeRennes/archipel/blob/main/libs/platform-blob-storage/src/strategies/aws-s3/AwsS3BlobStorageStrategy.ts#L134)
146
+
147
+ Downloads a blob object from the storage backend.
148
+
149
+ #### Type Parameters
150
+
151
+ | Type Parameter |
152
+ | ------ |
153
+ | `TData` |
154
+
155
+ #### Parameters
156
+
157
+ | Parameter | Type | Description |
158
+ | ------ | ------ | ------ |
159
+ | `request` | [`IBlobDownloadRequest`](Interface.IBlobDownloadRequest) | The download request describing the object to download. |
160
+
161
+ #### Returns
162
+
163
+ `Promise`\<[`IBlobDownloadResult`](Interface.IBlobDownloadResult)\<`TData`\>\>
164
+
165
+ The download result including payload and metadata.
166
+
167
+ #### Overrides
168
+
169
+ [`BlobStorageStrategyBase`](Class.BlobStorageStrategyBase).[`downloadObject`](Class.BlobStorageStrategyBase#downloadobject)
170
+
171
+ ***
172
+
173
+ ### uploadObject()
174
+
175
+ ```ts
176
+ uploadObject(request): Promise<IBlobUploadResult>;
177
+ ```
178
+
179
+ Defined in: [strategies/aws-s3/AwsS3BlobStorageStrategy.ts:89](https://github.com/RueDeRennes/archipel/blob/main/libs/platform-blob-storage/src/strategies/aws-s3/AwsS3BlobStorageStrategy.ts#L89)
180
+
181
+ Uploads a blob object to the storage backend.
182
+
183
+ #### Parameters
184
+
185
+ | Parameter | Type | Description |
186
+ | ------ | ------ | ------ |
187
+ | `request` | [`IBlobUploadRequest`](Interface.IBlobUploadRequest) | The upload request describing the object to upload. |
188
+
189
+ #### Returns
190
+
191
+ `Promise`\<[`IBlobUploadResult`](Interface.IBlobUploadResult)\>
192
+
193
+ The resulting upload metadata.
194
+
195
+ #### Overrides
196
+
197
+ [`BlobStorageStrategyBase`](Class.BlobStorageStrategyBase).[`uploadObject`](Class.BlobStorageStrategyBase#uploadobject)