@travetto/llm-support 8.0.0-alpha.20

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 (84) hide show
  1. package/README.md +101 -0
  2. package/__index__.ts +11 -0
  3. package/llm/consumer/INSTRUCTIONS.md +44 -0
  4. package/llm/maintainer/INSTRUCTIONS.md +37 -0
  5. package/package.json +56 -0
  6. package/resources/snippets/autograph-cli-orchestration.md +21 -0
  7. package/resources/snippets/code/aws-lambda-package-and-deploy.yml.tpl +36 -0
  8. package/resources/snippets/code/cache-enhancements.config.ts.tpl +6 -0
  9. package/resources/snippets/code/cache-enhancements.service.ts.tpl +16 -0
  10. package/resources/snippets/code/create-web-interceptor.ts.tpl +15 -0
  11. package/resources/snippets/code/create-web-route.controller.ts.tpl +15 -0
  12. package/resources/snippets/code/create-web-route.service.ts.tpl +8 -0
  13. package/resources/snippets/code/email-config.ts.tpl +6 -0
  14. package/resources/snippets/code/email-context-schema.ts.tpl +7 -0
  15. package/resources/snippets/code/email-create-template.mustache.tpl +2 -0
  16. package/resources/snippets/code/email-fixture.json.tpl +6 -0
  17. package/resources/snippets/code/email-preview-test.ts.tpl +14 -0
  18. package/resources/snippets/code/email-render-pipeline.ts.tpl +12 -0
  19. package/resources/snippets/code/email-send-controller.ts.tpl +18 -0
  20. package/resources/snippets/code/email-transport-provider.ts.tpl +8 -0
  21. package/resources/snippets/code/enable-auth-session.config.ts.tpl +34 -0
  22. package/resources/snippets/code/enable-auth-session.controller.ts.tpl +30 -0
  23. package/resources/snippets/code/enable-file-upload.config.ts.tpl +6 -0
  24. package/resources/snippets/code/enable-file-upload.controller.ts.tpl +16 -0
  25. package/resources/snippets/code/enable-linting.package.json.tpl +13 -0
  26. package/resources/snippets/code/generate-config.app-config.ts.tpl +7 -0
  27. package/resources/snippets/code/generate-config.application.yml.tpl +3 -0
  28. package/resources/snippets/code/generate-config.local.yml.tpl +2 -0
  29. package/resources/snippets/code/generate-test-suite.fixture.json.tpl +4 -0
  30. package/resources/snippets/code/generate-test-suite.unit.ts.tpl +11 -0
  31. package/resources/snippets/code/model-indexed.indexes.ts.tpl +14 -0
  32. package/resources/snippets/code/model-indexed.model.ts.tpl +8 -0
  33. package/resources/snippets/code/model-indexed.service.ts.tpl +15 -0
  34. package/resources/snippets/code/model-query.service.ts.tpl +18 -0
  35. package/resources/snippets/code/openapi-client-generation.readme.tpl +13 -0
  36. package/resources/snippets/code/openapi-client-generation.yml.tpl +20 -0
  37. package/resources/snippets/code/openapi-spec-pipeline.yml.tpl +21 -0
  38. package/resources/snippets/code/pack-docker-release.yml.tpl +21 -0
  39. package/resources/snippets/code/project-bootstrap.application.yml.tpl +2 -0
  40. package/resources/snippets/code/project-bootstrap.home-controller.ts.tpl +15 -0
  41. package/resources/snippets/code/project-bootstrap.home-service.ts.tpl +8 -0
  42. package/resources/snippets/code/project-bootstrap.monorepo.package.json.tpl +12 -0
  43. package/resources/snippets/code/project-bootstrap.package.json.tpl +18 -0
  44. package/resources/snippets/code/repo-version-release.yml.tpl +25 -0
  45. package/resources/snippets/code/rest-rpc-client.client.ts.tpl +8 -0
  46. package/resources/snippets/code/rest-rpc-client.index.ts.tpl +1 -0
  47. package/resources/snippets/code/workflow-cloudfront-deploy.yml.tpl +18 -0
  48. package/resources/snippets/code/workflow-gcp-deploy.yml.tpl +18 -0
  49. package/resources/snippets/core-aws-lambda-package-and-deploy.md +21 -0
  50. package/resources/snippets/core-email-compiler-pattern.md +21 -0
  51. package/resources/snippets/core-email-module-contract.md +21 -0
  52. package/resources/snippets/core-email-nodemailer-provider.md +21 -0
  53. package/resources/snippets/core-eslint-ruleset.md +21 -0
  54. package/resources/snippets/core-openapi-client-generation.md +21 -0
  55. package/resources/snippets/core-openapi-spec-pipeline.md +21 -0
  56. package/resources/snippets/core-pack-docker-release.md +21 -0
  57. package/resources/snippets/core-repo-version-release.md +21 -0
  58. package/resources/snippets/core-upload-pattern.md +21 -0
  59. package/resources/snippets/core-web-controller-pattern.md +23 -0
  60. package/resources/snippets/core-work-pool-pattern.md +23 -0
  61. package/resources/snippets/embracinglife-cloudfront-deploy.md +21 -0
  62. package/resources/snippets/embracinglife-gcp-deploy.md +21 -0
  63. package/resources/snippets/recipe-auth-google-oauth.md +21 -0
  64. package/resources/snippets/recipe-indexed-model-pattern.md +21 -0
  65. package/resources/snippets/recipe-upload-presigned.md +21 -0
  66. package/resources/snippets/todo-app-test-pattern.md +21 -0
  67. package/src/consumer-docs.ts +23 -0
  68. package/src/execute.ts +624 -0
  69. package/src/install-guidance.ts +187 -0
  70. package/src/mcp.ts +170 -0
  71. package/src/plan.ts +110 -0
  72. package/src/recommendation.ts +306 -0
  73. package/src/snippet-catalog.ts +80 -0
  74. package/src/snippet-shapes.ts +14 -0
  75. package/src/template-shapes.ts +13 -0
  76. package/src/tooling.ts +197 -0
  77. package/src/types.ts +215 -0
  78. package/src/workflow-guidance.ts +220 -0
  79. package/support/base-command.ts +57 -0
  80. package/support/cli.llm_support_execute.ts +80 -0
  81. package/support/cli.llm_support_mcp.ts +62 -0
  82. package/support/cli.llm_support_plan.ts +30 -0
  83. package/support/cli.llm_support_recommend.ts +34 -0
  84. package/support/cli.llm_support_status.ts +30 -0
package/README.md ADDED
@@ -0,0 +1,101 @@
1
+ <!-- This file was generated by @travetto/doc and should not be modified directly -->
2
+ <!-- Please modify https://github.com/travetto/travetto/tree/main/module/llm-support/DOC.tsx and execute "npx trv doc" to rebuild -->
3
+ # LLM Support
4
+
5
+ ## Task-oriented synthesized LLM guidance for Travetto modules.
6
+
7
+ The [LLM Support](https://github.com/travetto/travetto/tree/main/module/llm-support#readme "Task-oriented synthesized LLM guidance for Travetto modules.") module provides guided LLM assistance for Travetto projects. It helps you move from a target outcome to a concrete plan by combining bundle recommendations, workflow guidance, operation planning, and snippet selection.
8
+
9
+ ## Install
10
+ In a Travetto workspace, install the module and use the standard CLI entrypoint:
11
+
12
+ **Code: Install the module**
13
+ ```bash
14
+ npm install @travetto/llm-support
15
+ ```
16
+
17
+ The module is designed to be used through [Command Line Interface](https://github.com/travetto/travetto/tree/main/module/cli#readme "CLI infrastructure for Travetto framework") commands, so once the package is available you can invoke the LLM support workflow with `trv`.
18
+
19
+ ## Usage
20
+ The recommended flow is intentionally plan-first:
21
+ 1. Run `trv llm:support:recommend` to choose a bundle, workflow, category, or snippet set.
22
+ 1. Run `trv llm:support:plan` to preview the files and changes that would be produced.
23
+ 1. Run `trv llm:support:execute` to apply the selected operations, with dry-run behavior available by default.
24
+
25
+ **Code: Recommended flow**
26
+ ```bash
27
+ trv llm:support:recommend\ntrv llm:support:plan\ntrv llm:support:execute
28
+ ```
29
+
30
+ The recommendation command supports filtering by `bundles`, `workflows`, `categories`, and `snippet-tags`. The plan command focuses on selected `operations` and produces file-level change steps. Execution supports dry-run, overwrite, target directory selection, monorepo bootstrap selection (`--monorepo`), and several operation-specific hints such as route, controller, service, model, and email naming.
31
+
32
+ ## What It Supports
33
+ The module covers the core assistant paths used by Travetto projects:
34
+ * **Project bootstrap** - guided module and backend selection for a new application.
35
+ * **Web** - route, controller, service, interceptor, and client-oriented flows.
36
+ * **Auth** - session-backed identity and auth-web guided setup.
37
+ * **Model** - persistence, query, indexed, and backend selection guidance.
38
+ * **Upload** - direct upload and presigned URL support.
39
+ * **Workflow** - deployment-oriented GitHub workflow generation.
40
+ * **Quality** - linting and test suite setup.
41
+ * **Email** - templates, rendering, transport, preview, and send flows.
42
+ * **Test** - fixture and suite generation guidance.
43
+ * **Config** - configuration class and file generation.
44
+ * **Cache** - cache decorators and evictions workflows.
45
+
46
+ The available categories exposed by the CLI are `project`, `web`, `auth`, `model`, `upload`, `workflow`, `quality`, `email`, `test`, `config`, and `cache`.
47
+
48
+ ## Bundles And Workflows
49
+ Recommendations are grouped into install guidance bundles and workflow guidance:
50
+ * `web-api-baseline` - web application fundamentals with DI and schema support.
51
+ * `web-model-crud` - controller/service CRUD flows backed by model-query.
52
+ * `model-persistence-stack` - model persistence and adapter selection.
53
+ * `auth-enabled-web` - web auth with auth-web integration.
54
+ * `quality-lint-and-test` - linting and test guardrails.
55
+ * `email-generation-stack` - email template and delivery setup.
56
+ * `project-bootstrap` - guided new-project setup.
57
+ * `create-web-route` - route/controller/service generation workflow.
58
+ * `generate-web-model-crud` - model-backed CRUD generation workflow.
59
+
60
+ The recommendation output also includes snippets that match the selected operations and capability tags, so the generated plan stays tied to reusable implementation patterns.
61
+
62
+ ## Command Options
63
+ The CLI surface is designed for narrow, predictable selection:
64
+ * `--module` - scope recommendations to the active module.
65
+ * `--bundles` - choose specific install guidance bundles.
66
+ * `--workflows` - choose specific workflow guidance entries.
67
+ * `--operations` - choose specific operations for planning.
68
+ * `--categories` - filter by capability category.
69
+ * `--snippet-tags` - narrow the snippet catalog.
70
+ * `--include-excluded` - include excluded operations when you need the full catalog.
71
+ * `--monorepo` - when used with `project-bootstrap`, generate a workspace root and `packages/app` project layout.
72
+ * `--workspace-path` - customize the monorepo app location (for example `packages/api`).
73
+ * `--workspace-name` - customize the generated workspace package name used by root scripts.
74
+
75
+ That combination lets you start broad, then narrow to exactly the path you want before making changes.
76
+
77
+ ## MCP Integration
78
+ The module exposes a minimal stdio MCP entrypoint for tool-calling integrations:
79
+
80
+ **Code: Start MCP server**
81
+ ```bash
82
+ trv llm:support:mcp
83
+ ```
84
+
85
+ Supported methods are `initialize`, `tools/list`, and `tools/call`. Requests and responses are newline-delimited JSON-RPC 2.0 payloads.
86
+
87
+ **Code: MCP request examples**
88
+ ```json
89
+ {"jsonrpc":"2.0","id":1,"method":"initialize"}
90
+ {"jsonrpc":"2.0","id":2,"method":"tools/list"}
91
+ {"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"llm_support_plan","arguments":{"operations":["create-web-route"]}}}
92
+ ```
93
+
94
+ ## Contract Model
95
+ Contributor contract model for this module:
96
+ * Boundary contracts are schema classes first (inputs and outputs).
97
+ * Public type names are derived from classes instead of parallel interface trees.
98
+ * Runtime boundaries validate both inbound payloads and outbound responses.
99
+ * Tests should prefer schema bind+validate over custom shape guards when practical.
100
+
101
+ For execution and tooling helpers in this module, prefer non-assertion-safe binding where required by project rules.
package/__index__.ts ADDED
@@ -0,0 +1,11 @@
1
+ export * from './src/types.ts';
2
+ export * from './src/install-guidance.ts';
3
+ export * from './src/workflow-guidance.ts';
4
+ export * from './src/snippet-catalog.ts';
5
+ export * from './src/snippet-shapes.ts';
6
+ export * from './src/recommendation.ts';
7
+ export * from './src/plan.ts';
8
+ export * from './src/execute.ts';
9
+ export * from './src/tooling.ts';
10
+ export * from './src/mcp.ts';
11
+ export * from './src/consumer-docs.ts';
@@ -0,0 +1,44 @@
1
+ # LLM Support Instructions
2
+ Task-oriented guidance and operations for generating Travetto projects and features.
3
+
4
+ ## Core Intent
5
+ Use this module to discover and execute generation-ready guidance for:
6
+ - project bootstrap and model backend selection
7
+ - web/controller/service and interceptor creation
8
+ - auth, uploads, model-indexed/model-query flows
9
+ - lint/test quality enablement
10
+ - email generation and delivery sub-operations
11
+
12
+ ## Scope Decisions
13
+ Do not recommend excluded operations unless explicitly requested:
14
+ - log:config
15
+ - log:instrumentation
16
+ - eslint:profile
17
+ - test:mock-service
18
+
19
+ ## Usage Workflow
20
+ 1. Discover command shapes with `npx trv cli:schema` when uncertain.
21
+ 2. Request recommendations with `npx trv llm:support:recommend`.
22
+ 3. Select bundles/workflows/operations and generate plan-first changes.
23
+ 4. Validate with lint/test and targeted compile checks.
24
+
25
+ ## Framework Development Principles
26
+ - Prefer explicit contracts over implicit behavior; generated guidance should map to named operations, modules, and outcomes.
27
+ - Keep module boundaries clear: routing, service logic, persistence, and transport concerns should remain separable.
28
+ - Optimize for composability; recommendations should combine cleanly without hidden coupling.
29
+ - Default to safe behavior (plan-first, dry-run-first, minimal scope changes) and require explicit opt-in for destructive actions.
30
+ - Favor deterministic outputs so repeated runs with the same inputs produce equivalent guidance.
31
+
32
+ ## Best Practices
33
+ - Schema-first boundaries: define input/output contracts with schema classes at ownership boundaries.
34
+ - Dependency clarity: always separate required modules from optional adapters and explain why optional items exist.
35
+ - Compatibility discipline: preserve stable operation ids and tool names; additive changes are preferred over breaking renames.
36
+ - Validation before confidence: verify recommendations against command shape, then verify generated output with targeted tests/lint.
37
+ - Incremental adoption: start with baseline bundles and layer advanced features only when requirements justify complexity.
38
+ - Explain tradeoffs: when multiple stack choices exist, provide capability-based selection criteria (query, indexed, blob, expiry, etc.).
39
+ - Keep examples production-oriented: avoid toy guidance that skips error handling, configuration boundaries, or testability.
40
+
41
+ ## Change Quality Expectations
42
+ - Every new operation should include clear intent, required modules, optional modules, and verification checks.
43
+ - Every guidance expansion should include corresponding tests for discoverability and metadata integrity.
44
+ - Every consumer-facing behavior change should be reflected in these instructions so agent behavior stays aligned with framework expectations.
@@ -0,0 +1,37 @@
1
+ # Maintainer Notes
2
+
3
+ ## Module Role
4
+ This module owns LLM-oriented generation guidance for Travetto.
5
+
6
+ ## Implementation Rules
7
+ - Keep operation metadata explicit and testable.
8
+ - Track excluded operations in `src/recommendation.ts`.
9
+ - Keep install/workflow guidance focused on direct generation workflows.
10
+ - Avoid provider-specific integrations in this phase.
11
+
12
+ ## Framework Principles
13
+ - Treat llm-support as framework guidance infrastructure, not app-specific scaffolding.
14
+ - Preserve stable public contracts (operation ids, tool names, output shapes) unless a versioned compatibility change is planned.
15
+ - Keep guidance declarative and data-driven when possible to reduce execution branching and drift.
16
+ - Favor explicit policy over convention: codify exclusions, defaults, and verification expectations in source.
17
+
18
+ ## Engineering Best Practices
19
+ - Use schema classes for boundary contracts and runtime validation.
20
+ - Keep recommendations explainable: each operation should have intent, dependency rationale, and verification guidance.
21
+ - Maintain deterministic execution planning and artifact reporting for traceability.
22
+ - Prefer additive evolution of guidance catalogs and workflows over mutation of existing semantics.
23
+ - Ensure every new capability has at least one integrity test (metadata shape, discoverability, or execution coverage).
24
+ - Keep documentation synchronized with behavior changes in the same change set.
25
+
26
+ ## Compatibility And Change Discipline
27
+ - Breaking contract changes require an explicit compatibility note and migration guidance.
28
+ - New operations should avoid overlapping semantics unless distinction is documented.
29
+ - Excluded operations must remain visible in policy/tests, even when omitted from default recommendations.
30
+ - Guidance should remain monorepo-aware and avoid assumptions that only apply to single-package apps.
31
+
32
+ ## Catalog Evolution
33
+ When adding operations:
34
+ 1. Add type-safe operation metadata.
35
+ 2. Add workflow/install entries if module dependencies change.
36
+ 3. Add/update tests for filtering and exclusion behavior.
37
+ 4. Update consumer instructions if scope changes.
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@travetto/llm-support",
3
+ "version": "8.0.0-alpha.20",
4
+ "type": "module",
5
+ "description": "Task-oriented synthesized LLM guidance for Travetto modules.",
6
+ "keywords": [
7
+ "travetto",
8
+ "llm",
9
+ "docs",
10
+ "guidance"
11
+ ],
12
+ "main": "__index__.ts",
13
+ "homepage": "https://travetto.io",
14
+ "license": "MIT",
15
+ "author": {
16
+ "email": "travetto.framework@gmail.com",
17
+ "name": "Travetto Framework"
18
+ },
19
+ "files": [
20
+ "__index__.ts",
21
+ "src",
22
+ "support",
23
+ "resources",
24
+ "llm"
25
+ ],
26
+ "repository": {
27
+ "url": "git+https://github.com/travetto/travetto.git",
28
+ "directory": "module/llm-support"
29
+ },
30
+ "dependencies": {
31
+ "@travetto/runtime": "^8.0.0-alpha.0",
32
+ "@travetto/schema": "^8.0.0-alpha.0"
33
+ },
34
+ "peerDependencies": {
35
+ "@travetto/cli": "^8.0.0-alpha.0",
36
+ "@travetto/test": "^8.0.0-alpha.0"
37
+ },
38
+ "peerDependenciesMeta": {
39
+ "@travetto/cli": {
40
+ "optional": true
41
+ },
42
+ "@travetto/test": {
43
+ "optional": true
44
+ }
45
+ },
46
+ "travetto": {
47
+ "workspaceInclude": true,
48
+ "displayName": "LLM Support",
49
+ "roles": [
50
+ "build"
51
+ ]
52
+ },
53
+ "publishConfig": {
54
+ "access": "public"
55
+ }
56
+ }
@@ -0,0 +1,21 @@
1
+ <!-- json
2
+ {
3
+ "sourceId": "autograph-cli-orchestration",
4
+ "repositoryId": "autograph-typesetter",
5
+ "filePath": "support/cli.render.ts",
6
+ "capabilityTags": ["project", "cli", "orchestration"],
7
+ "operationIds": ["project-bootstrap"],
8
+ "applicability": ["sample-project"],
9
+ "notes": [
10
+ "Reference complex CLI orchestration and staged execution patterns."
11
+ ]
12
+ }
13
+ -->
14
+
15
+ # autograph-cli-orchestration
16
+
17
+ - Source: `support/cli.render.ts`
18
+ - Capability tags: project, cli, orchestration
19
+ - Applicability: sample-project
20
+ - Notes:
21
+ - Reference complex CLI orchestration and staged execution patterns.
@@ -0,0 +1,36 @@
1
+ name: lambda-deploy
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ workflow_dispatch:
7
+ inputs:
8
+ environment:
9
+ description: Deployment environment
10
+ required: true
11
+ default: dev
12
+
13
+ jobs:
14
+ package:
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ - uses: actions/setup-node@v4
19
+ with:
20
+ node-version: 20
21
+ - run: npm ci
22
+ - run: npx trv pack:lambda --output dist/lambda
23
+ - uses: actions/upload-artifact@v4
24
+ with:
25
+ name: lambda-package
26
+ path: dist/lambda
27
+
28
+ deploy:
29
+ needs: package
30
+ runs-on: ubuntu-latest
31
+ steps:
32
+ - uses: actions/download-artifact@v4
33
+ with:
34
+ name: lambda-package
35
+ path: dist/lambda
36
+ - run: echo "Deploy dist/lambda to AWS Lambda for ${{ inputs.environment }}"
@@ -0,0 +1,6 @@
1
+ import { Config } from '@travetto/config';
2
+
3
+ @Config('cache')
4
+ export class CacheConfig {
5
+ ttlMs = 30000;
6
+ }
@@ -0,0 +1,16 @@
1
+ import { Injectable } from '@travetto/di';
2
+
3
+ @Injectable()
4
+ export class CacheableService {
5
+ private readonly cache = new Map<string, string>();
6
+
7
+ getOrPopulate(key: string, producer: () => string): string {
8
+ const cached = this.cache.get(key);
9
+ if (cached !== undefined) {
10
+ return cached;
11
+ }
12
+ const value = producer();
13
+ this.cache.set(key, value);
14
+ return value;
15
+ }
16
+ }
@@ -0,0 +1,15 @@
1
+ import { Injectable } from '@travetto/di';
2
+ import { Interceptor } from '@travetto/web';
3
+
4
+ @Injectable()
5
+ export class RequestLoggingInterceptor implements Interceptor {
6
+ async intercept<T>(next: () => Promise<T>): Promise<T> {
7
+ const start = Date.now();
8
+ try {
9
+ return await next();
10
+ } finally {
11
+ const duration = Date.now() - start;
12
+ console.log('request-duration-ms', duration);
13
+ }
14
+ }
15
+ }
@@ -0,0 +1,15 @@
1
+ import { Inject } from '@travetto/di';
2
+ import { Controller, Get } from '@travetto/web';
3
+
4
+ import { {{serviceName}} } from '../service/{{serviceFile}}.ts';
5
+
6
+ @Controller('/{{routePath}}')
7
+ export class {{controllerName}} {
8
+ @Inject()
9
+ service: {{serviceName}};
10
+
11
+ @Get('/')
12
+ list(): { items: string[] } {
13
+ return { items: this.service.getItems() };
14
+ }
15
+ }
@@ -0,0 +1,8 @@
1
+ import { Injectable } from '@travetto/di';
2
+
3
+ @Injectable()
4
+ export class {{serviceName}} {
5
+ getItems(): string[] {
6
+ return ['item-1', 'item-2'];
7
+ }
8
+ }
@@ -0,0 +1,6 @@
1
+ import { Config } from '@travetto/config';
2
+
3
+ @Config('email')
4
+ export class EmailConfig {
5
+ from = 'noreply@example.com';
6
+ }
@@ -0,0 +1,7 @@
1
+ import { Schema } from '@travetto/schema';
2
+
3
+ @Schema()
4
+ export class {{emailType}}Context {
5
+ title: string;
6
+ message: string;
7
+ }
@@ -0,0 +1,2 @@
1
+ <h1>{{title}}</h1>
2
+ <p>{{message}}</p>
@@ -0,0 +1,6 @@
1
+ {
2
+ "to": "test@example.com",
3
+ "subject": "Sample Email",
4
+ "title": "Welcome",
5
+ "message": "Thanks for signing up"
6
+ }
@@ -0,0 +1,14 @@
1
+ import assert from 'node:assert';
2
+
3
+ import { Suite, Test } from '@travetto/test';
4
+
5
+ import { renderTransactionalEmail } from '../../src/email/render.ts';
6
+
7
+ @Suite()
8
+ class EmailPreviewTest {
9
+ @Test()
10
+ async preview() {
11
+ const html = await renderTransactionalEmail({ title: 'Hello', message: 'World' });
12
+ assert(html.includes('Hello'));
13
+ }
14
+ }
@@ -0,0 +1,12 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+
4
+ import mustache from 'mustache';
5
+
6
+ import type { TransactionalEmailContext } from './schema.ts';
7
+
8
+ export async function render{{renderName}}Email(ctx: TransactionalEmailContext): Promise<string> {
9
+ const tplPath = path.resolve(process.cwd(), 'src/email/templates/{{emailName}}.mustache');
10
+ const tpl = await fs.readFile(tplPath, 'utf8');
11
+ return mustache.render(tpl, ctx);
12
+ }
@@ -0,0 +1,18 @@
1
+ import { Inject } from '@travetto/di';
2
+ import { Body, Controller, Post } from '@travetto/web';
3
+
4
+ import { EmailProvider } from '../email/provider.ts';
5
+ import { renderTransactionalEmail } from '../email/render.ts';
6
+
7
+ @Controller('/{{routePath}}')
8
+ export class EmailController {
9
+ @Inject()
10
+ provider: EmailProvider;
11
+
12
+ @Post('/')
13
+ async send(@Body() body: { to: string; subject: string; title: string; message: string }): Promise<{ sent: true }> {
14
+ const html = await renderTransactionalEmail({ title: body.title, message: body.message });
15
+ await this.provider.send(body.to, body.subject, html);
16
+ return { sent: true };
17
+ }
18
+ }
@@ -0,0 +1,8 @@
1
+ import { Injectable } from '@travetto/di';
2
+
3
+ @Injectable()
4
+ export class EmailProvider {
5
+ async send(to: string, subject: string, html: string): Promise<void> {
6
+ console.log('email-send', { to, subject, size: html.length });
7
+ }
8
+ }
@@ -0,0 +1,34 @@
1
+ import { type Authenticator, AuthenticationError, type Principal } from '@travetto/auth';
2
+ import { SessionModelSymbol } from '@travetto/auth-session';
3
+ import { InjectableFactory } from '@travetto/di';
4
+ import { MemoryModelConfig, MemoryModelService } from '@travetto/model-memory';
5
+
6
+ export const BasicAuthSymbol = Symbol.for('AUTH_BASIC');
7
+
8
+ type User = { username: string; password: string };
9
+
10
+ export class AuthConfig {
11
+
12
+ @InjectableFactory(SessionModelSymbol)
13
+ static getSessionModel(): MemoryModelService {
14
+ return new MemoryModelService(new MemoryModelConfig());
15
+ }
16
+
17
+ @InjectableFactory(BasicAuthSymbol)
18
+ static getAuthenticator(): Authenticator<User> {
19
+ return {
20
+ authenticate(user): Principal {
21
+ if (user.username && user.password === 'password') {
22
+ return {
23
+ issuer: 'self',
24
+ id: user.username,
25
+ permissions: [],
26
+ details: {},
27
+ source: 'insecure'
28
+ };
29
+ }
30
+ throw new AuthenticationError('Unknown user');
31
+ }
32
+ };
33
+ }
34
+ }
@@ -0,0 +1,30 @@
1
+ import { type Principal } from '@travetto/auth';
2
+ import { Authenticated, Login, Logout } from '@travetto/auth-web';
3
+ import { ContextParam, Controller, Get, Post, WebResponse } from '@travetto/web';
4
+
5
+ import { BasicAuthSymbol } from './auth.config.ts';
6
+
7
+ @Controller('/auth')
8
+ export class AuthController {
9
+
10
+ @ContextParam()
11
+ user: Principal;
12
+
13
+ @Post('/login')
14
+ @Login(BasicAuthSymbol)
15
+ async login(): Promise<WebResponse> {
16
+ return WebResponse.redirect('/auth/self');
17
+ }
18
+
19
+ @Get('/self')
20
+ @Authenticated()
21
+ async self(): Promise<Principal> {
22
+ return this.user;
23
+ }
24
+
25
+ @Get('/logout')
26
+ @Logout()
27
+ async logout(): Promise<WebResponse> {
28
+ return WebResponse.redirect('/auth/self');
29
+ }
30
+ }
@@ -0,0 +1,6 @@
1
+ import { Config } from '@travetto/config';
2
+
3
+ @Config('upload')
4
+ export class UploadConfig {
5
+ maxSize = 10_000_000;
6
+ }
@@ -0,0 +1,16 @@
1
+ import { Controller, Post } from '@travetto/web';
2
+ import { type FileMap, Upload } from '@travetto/web-upload';
3
+
4
+ @Controller('/upload')
5
+ export class UploadController {
6
+
7
+ @Post('/')
8
+ uploadOne(@Upload() file: File): { name: string; size: number } {
9
+ return { name: file.name, size: file.size };
10
+ }
11
+
12
+ @Post('/batch')
13
+ async uploadMany(@Upload() files: FileMap): Promise<{ count: number }> {
14
+ return { count: Object.keys(files).length };
15
+ }
16
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "sample-app",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "lint:register": "trv eslint:register",
7
+ "lint": "npm run lint:register && trv eslint",
8
+ "lint:fix": "npm run lint:register && trv eslint --fix"
9
+ },
10
+ "devDependencies": {
11
+ "@travetto/eslint": "^8.0.0-alpha.20"
12
+ }
13
+ }
@@ -0,0 +1,7 @@
1
+ import { Config } from '@travetto/config';
2
+
3
+ @Config('app')
4
+ export class AppConfig {
5
+ name = 'app';
6
+ version = '1.0.0';
7
+ }
@@ -0,0 +1,3 @@
1
+ app:
2
+ name: {{projectName}}
3
+ version: 1.0.0
@@ -0,0 +1,2 @@
1
+ app:
2
+ name: local-app
@@ -0,0 +1,4 @@
1
+ {
2
+ "name": "example",
3
+ "enabled": true
4
+ }
@@ -0,0 +1,11 @@
1
+ import assert from 'node:assert';
2
+
3
+ import { Suite, Test } from '@travetto/test';
4
+
5
+ @Suite()
6
+ class ExampleUnitTest {
7
+ @Test()
8
+ basicAssertion() {
9
+ assert.strictEqual(1 + 1, 2);
10
+ }
11
+ }
@@ -0,0 +1,14 @@
1
+ import { keyedIndex, sortedIndex } from '@travetto/model-indexed';
2
+
3
+ import { {{modelName}} } from './{{modelFile}}.ts';
4
+
5
+ export const {{modelVar}}ByName = keyedIndex({{modelName}}, {
6
+ name: '{{modelFile}}_by_name',
7
+ key: { name: true }
8
+ });
9
+
10
+ export const {{modelVar}}ByNameCreatedAt = sortedIndex({{modelName}}, {
11
+ name: '{{modelFile}}_by_name_created_at',
12
+ key: { name: true },
13
+ sort: { createdAt: -1 }
14
+ });
@@ -0,0 +1,8 @@
1
+ import { Model } from '@travetto/model';
2
+
3
+ @Model()
4
+ export class {{modelName}} {
5
+ id: string;
6
+ name: string;
7
+ createdAt: Date;
8
+ }
@@ -0,0 +1,15 @@
1
+ import { Inject, Injectable } from '@travetto/di';
2
+ import type { ModelIndexedSupport } from '@travetto/model-indexed';
3
+
4
+ import { {{modelVar}}ByName } from '../model/{{modelFile}}.indexes.ts';
5
+ import { {{modelName}} } from '../model/{{modelFile}}.ts';
6
+
7
+ @Injectable()
8
+ export class {{modelName}}IndexedService {
9
+ @Inject()
10
+ source: ModelIndexedSupport;
11
+
12
+ getByName(name: string): Promise<{{modelName}}> {
13
+ return this.source.getByIndex({{modelName}}, {{modelVar}}ByName, { name });
14
+ }
15
+ }