@breadstone/archipel-mcp 0.0.10 → 0.0.13

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 (72) hide show
  1. package/data/guides/ai-text-generation.md +361 -0
  2. package/data/guides/analytics-and-error-tracking.md +189 -0
  3. package/data/guides/authentication-and-authorization.md +657 -0
  4. package/data/guides/blob-storage.md +242 -0
  5. package/data/guides/caching.md +255 -0
  6. package/data/guides/cryptography-and-otp.md +240 -0
  7. package/data/guides/database-setup.md +546 -0
  8. package/data/guides/document-generation.md +174 -0
  9. package/data/guides/email-delivery.md +196 -0
  10. package/data/guides/esigning-integration.md +231 -0
  11. package/data/guides/getting-started.md +351 -0
  12. package/data/guides/implementing-ports.md +317 -0
  13. package/data/guides/index.md +62 -0
  14. package/data/guides/mcp-server.md +222 -0
  15. package/data/guides/openapi-and-feature-discovery.md +266 -0
  16. package/data/guides/payments-and-feature-gating.md +244 -0
  17. package/data/guides/resource-management.md +352 -0
  18. package/data/guides/telemetry-and-observability.md +190 -0
  19. package/data/guides/testing.md +319 -0
  20. package/data/guides/tsdoc-guidelines.md +45 -0
  21. package/data/packages/platform-database/api/Class.DatabaseModule.md +3 -5
  22. package/data/packages/platform-database/api/Class.DatabaseService.md +5 -4
  23. package/data/packages/platform-database/api/Class.PrismaService.md +8 -7
  24. package/data/packages/platform-database/api/Interface.IDatabaseModuleConfig.md +13 -1
  25. package/data/packages/platform-database/api/Interface.IPrismaServiceOptions.md +22 -0
  26. package/data/packages/platform-database/api/Variable.DATABASE_MODULE_CONFIG.md +14 -0
  27. package/data/packages/platform-database/api/Variable.DB_ACCELERATE_URL.md +14 -0
  28. package/data/packages/platform-database/api/Variable.DB_AUTO_CONNECTION.md +14 -0
  29. package/data/packages/platform-database/api/Variable.DB_OPTIMIZE.md +14 -0
  30. package/data/packages/platform-database/api/Variable.DB_OPTIMIZE_API_KEY.md +14 -0
  31. package/data/packages/platform-database/api/Variable.PLATFORM_DATABASE_CONFIG_ENTRIES.md +14 -0
  32. package/data/packages/platform-database/api/index.md +13 -1
  33. package/data/packages/platform-database/index.md +41 -18
  34. package/data/packages/platform-openapi/api/Class.SwaggerFeatureDiscovery.md +10 -6
  35. package/data/packages/platform-telemetry/api/Class.NoopTelemetryFacade.md +10 -10
  36. package/data/packages/platform-telemetry/api/Interface.ITelemetryFacade.md +1 -1
  37. package/package.json +1 -1
  38. package/src/GuidesLoader.d.ts +13 -0
  39. package/src/GuidesLoader.js +81 -0
  40. package/src/main.js +36 -198
  41. package/src/models/IGuideDoc.d.ts +15 -0
  42. package/src/models/IGuideDoc.js +3 -0
  43. package/src/tools/registerGetConfigPatternTool.d.ts +5 -0
  44. package/src/tools/registerGetConfigPatternTool.js +15 -0
  45. package/src/tools/registerGetDtoPatternTool.d.ts +5 -0
  46. package/src/tools/registerGetDtoPatternTool.js +15 -0
  47. package/src/tools/registerGetErrorHandlingPatternTool.d.ts +5 -0
  48. package/src/tools/registerGetErrorHandlingPatternTool.js +15 -0
  49. package/src/tools/registerGetGuardPatternTool.d.ts +5 -0
  50. package/src/tools/registerGetGuardPatternTool.js +15 -0
  51. package/src/tools/registerGetGuideTool.d.ts +6 -0
  52. package/src/tools/registerGetGuideTool.js +32 -0
  53. package/src/tools/registerGetMappingPatternTool.d.ts +5 -0
  54. package/src/tools/registerGetMappingPatternTool.js +34 -0
  55. package/src/tools/registerGetModulePatternTool.d.ts +5 -0
  56. package/src/tools/registerGetModulePatternTool.js +29 -0
  57. package/src/tools/registerGetPackageDocTool.d.ts +6 -0
  58. package/src/tools/registerGetPackageDocTool.js +43 -0
  59. package/src/tools/registerGetQueryPatternTool.d.ts +5 -0
  60. package/src/tools/registerGetQueryPatternTool.js +29 -0
  61. package/src/tools/registerGetRepositoryPatternTool.d.ts +5 -0
  62. package/src/tools/registerGetRepositoryPatternTool.js +29 -0
  63. package/src/tools/registerGetTestingPatternTool.d.ts +5 -0
  64. package/src/tools/registerGetTestingPatternTool.js +15 -0
  65. package/src/tools/registerListGuidesTool.d.ts +6 -0
  66. package/src/tools/registerListGuidesTool.js +22 -0
  67. package/src/tools/registerListPackagesTool.d.ts +6 -0
  68. package/src/tools/registerListPackagesTool.js +20 -0
  69. package/src/tools/registerSearchDocsTool.d.ts +6 -0
  70. package/src/tools/registerSearchDocsTool.js +35 -0
  71. package/src/tools/registerSearchGuidesTool.d.ts +6 -0
  72. package/src/tools/registerSearchGuidesTool.js +28 -0
@@ -0,0 +1,244 @@
1
+ ---
2
+ title: Payments & Feature Gating
3
+ description: Integrate payment providers (Stripe, Paddle, LemonSqueezy, Mollie) and gate features by subscription tier.
4
+ order: 9
5
+ ---
6
+
7
+ # Payments & Feature Gating
8
+
9
+ This guide covers payment integration with `platform-payments`: choosing a payment provider, handling webhooks, gating features by subscription tier, and tracking feature usage.
10
+
11
+ ---
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ yarn add @breadstone/archipel-platform-payments
17
+ ```
18
+
19
+ ---
20
+
21
+ ## Choose a Payment Provider
22
+
23
+ `platform-payments` normalizes payment operations across multiple providers. You implement the `PaymentClientPort` for your chosen provider:
24
+
25
+ | Provider | What it provides |
26
+ | ---------------- | --------------------------------------- |
27
+ | **Stripe** | Cards, subscriptions, invoices |
28
+ | **Paddle** | SaaS billing, tax handling |
29
+ | **LemonSqueezy** | Digital products, memberships |
30
+ | **Mollie** | European payment methods, subscriptions |
31
+
32
+ ---
33
+
34
+ ## Module Registration
35
+
36
+ ```typescript
37
+ import { Module } from '@nestjs/common';
38
+ import { PaymentModule } from '@breadstone/archipel-platform-payments';
39
+ import { StripePaymentClient } from './adapters/StripePaymentClient';
40
+ import { PrismaFeatureAccessAdapter } from './adapters/PrismaFeatureAccessAdapter';
41
+ import { STRIPE_CONFIG_ENTRIES } from './env';
42
+
43
+ @Module({
44
+ imports: [
45
+ PaymentModule.register({
46
+ paymentClient: StripePaymentClient,
47
+ featureAccess: PrismaFeatureAccessAdapter,
48
+ configEntries: [...STRIPE_CONFIG_ENTRIES],
49
+ }),
50
+ ],
51
+ })
52
+ export class AppModule {}
53
+ ```
54
+
55
+ ---
56
+
57
+ ## Implementing the Payment Client Port
58
+
59
+ The `PaymentClientPort` defines the contract between the library and your payment provider. Implement it for your chosen provider:
60
+
61
+ ```typescript
62
+ import { Injectable } from '@nestjs/common';
63
+ import {
64
+ PaymentClientPort,
65
+ type INormalizedCheckoutSession,
66
+ type INormalizedSubscription,
67
+ type INormalizedWebhookEvent,
68
+ } from '@breadstone/archipel-platform-payments';
69
+
70
+ @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
75
+ }
76
+
77
+ public async getSubscription(subscriptionId: string): Promise<INormalizedSubscription | null> {
78
+ // Fetch subscription details from Stripe
79
+ // Return normalized subscription data
80
+ }
81
+
82
+ public async handleWebhook(payload: Buffer, signature: string): Promise<INormalizedWebhookEvent> {
83
+ // Verify webhook signature and normalize the event
84
+ }
85
+ }
86
+ ```
87
+
88
+ The library uses **normalized models** (`INormalizedSubscription`, `INormalizedCheckoutSession`, `INormalizedWebhookEvent`) so your application code works identically regardless of the payment provider.
89
+
90
+ ---
91
+
92
+ ## Feature Gating
93
+
94
+ The `FeatureAccessPort` lets you restrict functionality based on subscription tiers.
95
+
96
+ ### Step 1: Implement the Port
97
+
98
+ ```typescript
99
+ import { Injectable } from '@nestjs/common';
100
+ import { FeatureAccessPort } from '@breadstone/archipel-platform-payments';
101
+ import { PrismaService } from '@breadstone/archipel-platform-database';
102
+
103
+ @Injectable()
104
+ export class PrismaFeatureAccessAdapter extends FeatureAccessPort {
105
+ private readonly _prisma: PrismaService;
106
+
107
+ constructor(prisma: PrismaService) {
108
+ super();
109
+ this._prisma = prisma;
110
+ }
111
+
112
+ public async hasAccess(userId: string, feature: string): Promise<boolean> {
113
+ const subscription = await this._prisma.subscription.findFirst({
114
+ where: { userId, status: 'active' },
115
+ include: { plan: true },
116
+ });
117
+
118
+ if (!subscription) {
119
+ return false;
120
+ }
121
+
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() },
128
+ });
129
+ }
130
+
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 } },
134
+ });
135
+ }
136
+ }
137
+ ```
138
+
139
+ ### Step 2: Use the Feature Guard
140
+
141
+ The `FeatureGuard` decorator restricts endpoints to users with access to specific features:
142
+
143
+ ```typescript
144
+ import { Controller, Post, UseGuards } from '@nestjs/common';
145
+ import { JwtAuthGuard } from '@breadstone/archipel-platform-authentication';
146
+ import { FeatureGuard } from '@breadstone/archipel-platform-payments';
147
+
148
+ @Controller('exports')
149
+ @UseGuards(JwtAuthGuard)
150
+ export class ExportController {
151
+ @Post('pdf')
152
+ @FeatureGuard('export:pdf')
153
+ public async exportPdf(): Promise<void> {
154
+ // Only accessible to users with 'export:pdf' feature
155
+ }
156
+
157
+ @Post('csv')
158
+ @FeatureGuard('export:csv')
159
+ public async exportCsv(): Promise<void> {
160
+ // Only accessible to users with 'export:csv' feature
161
+ }
162
+ }
163
+ ```
164
+
165
+ ### Step 3: Track Usage with Interceptor
166
+
167
+ `FeatureUsageInterceptor` automatically records feature usage after a successful request:
168
+
169
+ ```typescript
170
+ import { Controller, Post, UseGuards, UseInterceptors } from '@nestjs/common';
171
+ import { JwtAuthGuard } from '@breadstone/archipel-platform-authentication';
172
+ import { FeatureGuard, FeatureUsageInterceptor } from '@breadstone/archipel-platform-payments';
173
+
174
+ @Controller('ai')
175
+ @UseGuards(JwtAuthGuard)
176
+ export class AiController {
177
+ @Post('generate')
178
+ @FeatureGuard('ai:generate')
179
+ @UseInterceptors(FeatureUsageInterceptor)
180
+ public async generate(): Promise<string> {
181
+ // Usage is recorded automatically after success
182
+ return 'generated content';
183
+ }
184
+ }
185
+ ```
186
+
187
+ ---
188
+
189
+ ## Handling Webhooks
190
+
191
+ Payment providers send webhook events for subscription changes, payment failures, etc. Create a dedicated webhook controller:
192
+
193
+ ```typescript
194
+ import { Controller, Post, Req, Headers, RawBodyRequest } from '@nestjs/common';
195
+ import { PaymentClientPort } from '@breadstone/archipel-platform-payments';
196
+ import { Request } from 'express';
197
+
198
+ @Controller('webhooks/payments')
199
+ export class PaymentWebhookController {
200
+ private readonly _paymentClient: PaymentClientPort;
201
+
202
+ constructor(paymentClient: PaymentClientPort) {
203
+ this._paymentClient = paymentClient;
204
+ }
205
+
206
+ @Post()
207
+ public async handleWebhook(
208
+ @Req() request: RawBodyRequest<Request>,
209
+ @Headers('stripe-signature') signature: string,
210
+ ): Promise<void> {
211
+ const event = await this._paymentClient.handleWebhook(request.rawBody!, signature);
212
+
213
+ switch (event.type) {
214
+ case 'subscription.created':
215
+ case 'subscription.updated':
216
+ // Update subscription in your database
217
+ break;
218
+ case 'subscription.deleted':
219
+ // Handle cancellation
220
+ break;
221
+ case 'payment.failed':
222
+ // Notify user, retry logic
223
+ break;
224
+ }
225
+ }
226
+ }
227
+ ```
228
+
229
+ ---
230
+
231
+ ## Best Practices
232
+
233
+ 1. **Define feature keys as constants.** Never use inline strings for feature identifiers.
234
+ 2. **Verify webhook signatures.** Always validate the webhook payload before processing.
235
+ 3. **Use normalized models.** Don't leak provider-specific types into your business logic.
236
+ 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.
238
+
239
+ ---
240
+
241
+ ## Next Steps
242
+
243
+ - See the [Authentication & Authorization](/guides/authentication-and-authorization) guide for securing payment endpoints
244
+ - Browse the [platform-payments API reference](/packages/platform-payments/api/) for complete method signatures
@@ -0,0 +1,352 @@
1
+ ---
2
+ title: Resource Management
3
+ description: Register and load files (templates, assets, configs) from disk, blob storage, or memory using the strategy-based ResourceManager.
4
+ order: 17
5
+ ---
6
+
7
+ # Resource Management
8
+
9
+ This guide covers working with `ResourceManager` from `platform-core`: registering file strategies, loading resources at runtime, and using the built-in caching layer. If your application ships email templates, HTML pages, CSS/JS assets, or any other file-based content, this is how you make it available to services.
10
+
11
+ ---
12
+
13
+ ## Installation
14
+
15
+ `ResourceManager` is part of `platform-core` — no extra package needed.
16
+
17
+ ```bash
18
+ yarn add @breadstone/archipel-platform-core
19
+ ```
20
+
21
+ ---
22
+
23
+ ## Overview: Strategy Pattern
24
+
25
+ `ResourceManager` does not load files directly. It delegates to one or more **strategies**, tried in priority order. The first strategy that can resolve the key wins.
26
+
27
+ | Strategy | Source | Sync | Async | When to use |
28
+ | -------------------------- | ------------------ | ---- | ----- | -------------------------------------- |
29
+ | `FileResourceStrategy` | Local filesystem | Yes | Yes | Templates, assets bundled with the app |
30
+ | `BlobResourceStrategy` | Cloud blob storage | No | Yes | User-uploaded templates, dynamic files |
31
+ | `EmbeddedResourceStrategy` | In-memory map | Yes | Yes | Fallback content, test fixtures |
32
+
33
+ Strategies are evaluated in the order you provide them. A typical setup:
34
+
35
+ ```
36
+ FileResourceStrategy → BlobResourceStrategy → EmbeddedResourceStrategy
37
+ ```
38
+
39
+ If the file exists on disk, it's served immediately. If not, blob storage is checked. If that also misses, the embedded fallback is returned. If nothing matches, a `ResourceNotFoundError` is thrown.
40
+
41
+ ---
42
+
43
+ ## Module Registration
44
+
45
+ `ResourceModule` is a global module. Register it once in your root module with `forRoot()`:
46
+
47
+ ```typescript
48
+ import { Module } from '@nestjs/common';
49
+ import { join } from 'node:path';
50
+ import { ResourceModule, FileResourceStrategy, EmbeddedResourceStrategy } from '@breadstone/archipel-platform-core';
51
+
52
+ @Module({
53
+ imports: [
54
+ ResourceModule.forRoot({
55
+ strategies: [
56
+ new FileResourceStrategy({
57
+ basePaths: [
58
+ join(__dirname, 'assets'),
59
+ join(__dirname, '..', 'node_modules', '@breadstone', 'archipel-platform-core', 'assets'),
60
+ join(__dirname, '..', 'node_modules', '@breadstone', 'archipel-platform-openapi', 'assets'),
61
+ join(__dirname, '..', 'node_modules', '@breadstone', 'archipel-platform-mailing', 'assets'),
62
+ ],
63
+ includeSubfolders: true,
64
+ }),
65
+ new EmbeddedResourceStrategy({
66
+ 'fallback.html': '<html><body>Service unavailable</body></html>',
67
+ }),
68
+ ],
69
+ debug: false,
70
+ }),
71
+ ],
72
+ })
73
+ export class AppModule {}
74
+ ```
75
+
76
+ ### Configuration Options
77
+
78
+ | Option | Type | Default | Description |
79
+ | ------------ | ---------------------------------------- | ------- | ------------------------------------------------ |
80
+ | `strategies` | `IResourceStrategy[]` | — | Strategies in priority order. Required. |
81
+ | `cache` | `ILayeredCache<string, IResourceResult>` | — | Optional cache instance from `platform-caching`. |
82
+ | `cacheTtl` | `number` (seconds) | `3600` | TTL for cached resources. |
83
+ | `debug` | `boolean` | `false` | Log all available resources on startup. |
84
+
85
+ ---
86
+
87
+ ## Loading Resources
88
+
89
+ Inject `ResourceManager` and call its load methods. Every method has a `try*` variant that returns `undefined` instead of throwing.
90
+
91
+ ```typescript
92
+ import { Injectable } from '@nestjs/common';
93
+ import { ResourceManager } from '@breadstone/archipel-platform-core';
94
+
95
+ @Injectable()
96
+ export class InvoiceService {
97
+ private readonly _resourceManager: ResourceManager;
98
+
99
+ constructor(resourceManager: ResourceManager) {
100
+ this._resourceManager = resourceManager;
101
+ }
102
+
103
+ public async generateInvoice(): Promise<string> {
104
+ // Load an HTML template as string
105
+ const template = await this._resourceManager.loadAsStringAsync('invoice-template.html');
106
+
107
+ // ... fill template with data
108
+ return template;
109
+ }
110
+ }
111
+ ```
112
+
113
+ ### Method Reference
114
+
115
+ | Method | Returns | Throws on miss | Notes |
116
+ | ------------------------- | --------------------------------------- | -------------- | ----------------------------- |
117
+ | `load(key)` | `IResourceResult` | Yes | Sync |
118
+ | `tryLoad(key)` | `IResourceResult \| undefined` | No | Sync |
119
+ | `loadAsync(key)` | `Promise<IResourceResult>` | Yes | Required for blob strategy |
120
+ | `tryLoadAsync(key)` | `Promise<IResourceResult \| undefined>` | No | |
121
+ | `loadAsString(key, enc?)` | `string` | Yes | Defaults to `utf-8` |
122
+ | `loadAsStringAsync(key)` | `Promise<string>` | Yes | |
123
+ | `loadAsBuffer(key)` | `Buffer \| undefined` | No | |
124
+ | `loadAsBufferAsync(key)` | `Promise<Buffer \| undefined>` | No | |
125
+ | `exists(key)` | `boolean` | — | Sync |
126
+ | `existsAsync(key)` | `Promise<boolean>` | — | |
127
+ | `invalidate(key)` | `void` | — | Clears cache for key |
128
+ | `invalidateAsync(key)` | `Promise<void>` | — | |
129
+ | `whatDoIHave()` | `void` | — | Logs all resources to console |
130
+
131
+ ### The `IResourceResult` Object
132
+
133
+ When you use the full `load()` / `tryLoad()` methods you get rich metadata alongside the content:
134
+
135
+ ```typescript
136
+ interface IResourceResult {
137
+ content: Buffer;
138
+ metadata: IResourceMetadata;
139
+ }
140
+
141
+ interface IResourceMetadata {
142
+ key: string; // Resource identifier you requested
143
+ path: string; // Resolved file / blob path
144
+ mimeType: string; // e.g. 'text/html', 'application/pdf'
145
+ size: number; // Size in bytes
146
+ encoding?: string; // e.g. 'utf-8'
147
+ source: string; // Strategy name that resolved it (e.g. 'file')
148
+ cachedAt?: Date; // Present when served from cache
149
+ }
150
+ ```
151
+
152
+ ---
153
+
154
+ ## Strategy Details
155
+
156
+ ### FileResourceStrategy
157
+
158
+ Searches one or more base paths on the filesystem. Each path is tried in order. If `includeSubfolders` is enabled, a recursive glob search runs as fallback when the direct lookup misses.
159
+
160
+ ```typescript
161
+ new FileResourceStrategy({
162
+ basePaths: [join(__dirname, 'assets'), join(__dirname, 'templates')],
163
+ includeSubfolders: true,
164
+ defaultEncoding: 'utf-8',
165
+ });
166
+ ```
167
+
168
+ The key you pass to `load()` is the **filename** (e.g. `'invoice-template.html'`). The strategy joins it with each base path and checks if the file exists.
169
+
170
+ ### BlobResourceStrategy
171
+
172
+ Loads from cloud blob storage via a minimal `IBlobServiceAdapter` interface. This is **async-only** — calling `load()` (sync) will throw.
173
+
174
+ ```typescript
175
+ import { BlobResourceStrategy, IBlobServiceAdapter } from '@breadstone/archipel-platform-core';
176
+
177
+ const blobAdapter: IBlobServiceAdapter = {
178
+ async downloadFile(key: string) {
179
+ // Your blob storage client logic here
180
+ return { data: buffer, contentType: 'text/html' };
181
+ },
182
+ };
183
+
184
+ new BlobResourceStrategy(blobAdapter, {
185
+ keyPrefix: 'templates/',
186
+ });
187
+ ```
188
+
189
+ With `keyPrefix: 'templates/'`, a call to `loadAsync('invoice.html')` resolves to the blob key `templates/invoice.html`.
190
+
191
+ ### EmbeddedResourceStrategy
192
+
193
+ Stores resources as plain strings in memory. Useful for fallback content or test fixtures.
194
+
195
+ ```typescript
196
+ new EmbeddedResourceStrategy({
197
+ 'maintenance.html': '<h1>We are down for maintenance</h1>',
198
+ 'default-logo.svg': '<svg>...</svg>',
199
+ });
200
+ ```
201
+
202
+ You can also register resources dynamically after construction:
203
+
204
+ ```typescript
205
+ const embedded = new EmbeddedResourceStrategy();
206
+ embedded.register('dynamic-content.txt', 'Hello World');
207
+ ```
208
+
209
+ ---
210
+
211
+ ## Real-World Examples
212
+
213
+ ### Email Templates
214
+
215
+ `platform-mailing` uses `ResourceManager` to load HTML email templates:
216
+
217
+ ```typescript
218
+ // In MailModule.ts — factory creates the strategy
219
+ ResourceModule.forRoot({
220
+ strategies: [
221
+ new FileResourceStrategy({
222
+ basePaths: [
223
+ join(__dirname, 'assets'), // App-level overrides
224
+ join(__dirname, '..', 'node_modules', '@breadstone', 'archipel-platform-mailing', 'assets'),
225
+ ],
226
+ }),
227
+ ],
228
+ });
229
+ ```
230
+
231
+ Templates like `AuthVerify.html`, `AuthRegister.html`, and `AuthForgotPassword.html` are loaded by key:
232
+
233
+ ```typescript
234
+ const template = await this._resourceManager.loadAsStringAsync('AuthVerify.html');
235
+ ```
236
+
237
+ By listing your own `assets/` first, you can override built-in templates without modifying the library.
238
+
239
+ ### Swagger UI Assets
240
+
241
+ `platform-openapi` loads its theme files (`swagger.css`, `swagger.js`, `swagger.html`) through the same mechanism:
242
+
243
+ ```typescript
244
+ const css = this._resourceManager.loadAsString('swagger.css');
245
+ const js = this._resourceManager.loadAsString('swagger.js');
246
+ ```
247
+
248
+ ### Application Index Page
249
+
250
+ `platform-core`'s `HostService` loads `index.html` as a template for the root route:
251
+
252
+ ```typescript
253
+ const html = await this._resourceManager.loadAsStringAsync('index.html');
254
+ ```
255
+
256
+ ---
257
+
258
+ ## Caching
259
+
260
+ Pass a cache instance from `platform-caching` to avoid repeated file reads or blob downloads:
261
+
262
+ ```typescript
263
+ import { LayeredCacheFactory } from '@breadstone/archipel-platform-caching';
264
+
265
+ ResourceModule.forRoot({
266
+ strategies: [
267
+ new FileResourceStrategy({ basePaths: [...] }),
268
+ ],
269
+ cache: LayeredCacheFactory.create<string, IResourceResult>({
270
+ maxSize: 100,
271
+ }),
272
+ cacheTtl: 1800, // 30 minutes
273
+ })
274
+ ```
275
+
276
+ - On load: cache is checked first. On hit, the resource is returned immediately.
277
+ - On miss: strategies are tried, and the result is cached for future requests.
278
+ - Use `invalidate(key)` or `invalidateAsync(key)` to clear a cached resource (e.g. after an upload replaces a template).
279
+
280
+ ---
281
+
282
+ ## Debugging
283
+
284
+ Enable `debug: true` in the module config to have `ResourceManager` call `whatDoIHave()` at startup. This logs every resource available through every strategy — helpful when a resource isn't found and you need to verify the base paths.
285
+
286
+ ```
287
+ [ResourceManager] ResourceManager initialized with 2 strategies: file, embedded
288
+ [FileResourceStrategy] Available resources:
289
+ - index.html (text/html, 2.4 KB) → /app/dist/assets/index.html
290
+ - swagger.css (text/css, 1.1 KB) → /app/dist/assets/swagger.css
291
+ ...
292
+ [EmbeddedResourceStrategy] Available resources:
293
+ - fallback.html (text/html, 52 B) → embedded
294
+ ```
295
+
296
+ ---
297
+
298
+ ## Writing a Custom Strategy
299
+
300
+ Implement `IResourceStrategy` to support a new source (e.g. a database, S3 directly, or a remote API):
301
+
302
+ ```typescript
303
+ import { IResourceStrategy, IResourceResult } from '@breadstone/archipel-platform-core';
304
+
305
+ export class DatabaseResourceStrategy implements IResourceStrategy {
306
+ public get name(): string {
307
+ return 'database';
308
+ }
309
+
310
+ public load(key: string): IResourceResult | undefined {
311
+ // Sync lookup — return undefined if not found
312
+ }
313
+
314
+ public async loadAsync(key: string): Promise<IResourceResult | undefined> {
315
+ // Async lookup — return undefined if not found
316
+ }
317
+
318
+ public exists(key: string): boolean {
319
+ // ...
320
+ }
321
+
322
+ public async existsAsync(key: string): Promise<boolean> {
323
+ // ...
324
+ }
325
+
326
+ public whatDoIHave(): void {
327
+ // Log available resources for debugging
328
+ }
329
+ }
330
+ ```
331
+
332
+ Then add it to the strategies array:
333
+
334
+ ```typescript
335
+ ResourceModule.forRoot({
336
+ strategies: [
337
+ new FileResourceStrategy({ basePaths: [...] }),
338
+ new DatabaseResourceStrategy(prisma),
339
+ ],
340
+ })
341
+ ```
342
+
343
+ ---
344
+
345
+ ## Checklist
346
+
347
+ - [ ] `ResourceModule.forRoot()` registered in the root module
348
+ - [ ] Base paths include library asset directories for all used packages
349
+ - [ ] App-level overrides listed **before** library paths in `basePaths`
350
+ - [ ] Async methods used when `BlobResourceStrategy` is in the chain
351
+ - [ ] Cache configured for production (avoids repeated disk/network reads)
352
+ - [ ] `debug: true` used during development to verify resource availability