ras-stack 0.1.0 → 0.2.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 (2) hide show
  1. package/README.md +91 -37
  2. package/package.json +2 -2
package/README.md CHANGED
@@ -1,53 +1,68 @@
1
1
  # ras-stack
2
2
 
3
- Composable full-stack primitives shared across Richard Solomou's applications.
3
+ **The small pieces of full-stack plumbing I reuse across my TypeScript applications.**
4
4
 
5
- `ras-stack` removes repeated infrastructure decisions without wrapping or replacing the libraries underneath. Applications continue to call Better Auth, TanStack Start, Drizzle, React Query, and Centrifugo directly. They can adopt any primitive independently and retain ownership of schemas, migrations, authorization, plugins, routes, and domain events.
5
+ [![npm](https://img.shields.io/npm/v/ras-stack)](https://www.npmjs.com/package/ras-stack) [![Build](https://img.shields.io/github/actions/workflow/status/richardsolomou/ras-stack/ci.yml?branch=main)](https://github.com/richardsolomou/ras-stack/actions/workflows/ci.yml) [![License](https://img.shields.io/github/license/richardsolomou/ras-stack)](LICENSE)
6
+
7
+ I build several applications with the same TypeScript stack. They kept growing slightly different copies of the same code for session settings, origin checks, email delivery, realtime publication, resumable uploads, health checks, and project configuration.
8
+
9
+ `ras-stack` is the shared home for that plumbing. It is a personal library, published in case the code or the way it is split up is useful to someone else.
10
+
11
+ ## What it is
12
+
13
+ The package contains independent helpers for common application infrastructure:
14
+
15
+ - **Authentication:** secure defaults and utilities for sessions, rate limits, secrets, social providers, tokens, and trusted origins.
16
+ - **Server requests:** same-origin mutation guards, error-normalizing RPC wrappers, canonical-host redirects, and health responses.
17
+ - **Realtime:** Centrifugo token signing and a bounded publisher with retries and graceful shutdown.
18
+ - **Email:** SMTP environment parsing and a small Nodemailer delivery interface.
19
+ - **Uploads:** a promise-based wrapper around resumable `tus-js-client` uploads.
20
+ - **Project configuration:** shared TypeScript and Oxlint bases.
21
+
22
+ Each area has its own import path. An application can use one without adopting the rest.
23
+
24
+ ## What it is not
25
+
26
+ This is not a framework, starter, application template, or complete authentication system. It does not own an application's database schema, migrations, routes, authorization rules, email templates, upload policy, or realtime event names.
27
+
28
+ The libraries underneath remain available normally. Applications still configure and call Better Auth, TanStack Start, Drizzle, Nodemailer, `tus-js-client`, and Centrifugo directly. The helpers only centralize the parts that would otherwise be copied unchanged.
6
29
 
7
30
  ## Install
8
31
 
32
+ `ras-stack` requires Node 24.
33
+
9
34
  ```sh
10
35
  pnpm add ras-stack
11
36
  ```
12
37
 
13
- The email and upload entrypoints use optional peer dependencies. Install only the integration an application consumes:
38
+ Nodemailer and `tus-js-client` are optional peer dependencies. Install one only when using its entrypoint:
14
39
 
15
40
  ```sh
16
41
  pnpm add nodemailer
17
42
  pnpm add tus-js-client
18
43
  ```
19
44
 
20
- The package requires Node 24 and publishes separate entrypoints so an application does not load unused integrations:
21
-
22
- - `ras-stack/auth` — secrets, origin policy, provider credentials, random tokens, sessions, and rate limits.
23
- - `ras-stack/email` — SMTP environment parsing, Nodemailer transport creation, and delivery.
24
- - `ras-stack/realtime` — Centrifugo publication, bounded retries/backpressure, shutdown, and signed tokens.
25
- - `ras-stack/server` — canonical redirects, health responses, and framework-injected RPC wrappers.
26
- - `ras-stack/uploads` — configurable tus uploads and error-response parsing.
27
- - `ras-stack/config/*` — inheritable Oxlint and TypeScript configuration.
45
+ ## Authentication and request security
28
46
 
29
- ## Auth
30
-
31
- The auth entrypoint provides independent options and utilities rather than an auth factory:
47
+ The auth entrypoint provides options and utilities rather than an auth factory. The application keeps its complete Better Auth configuration:
32
48
 
33
49
  ```ts
34
- import { standardRateLimitOptions, standardSessionOptions, trustedOrigins } from 'ras-stack/auth'
35
50
  import { betterAuth } from 'better-auth'
51
+ import { standardRateLimitOptions, standardSessionOptions, trustedOrigins } from 'ras-stack/auth'
36
52
 
37
53
  const auth = betterAuth({
38
54
  database,
39
55
  plugins,
40
56
  session: standardSessionOptions(),
41
57
  rateLimit: standardRateLimitOptions({ '/sign-up/email': { window: 60, max: 10 } }),
42
- trustedOrigins: trustedOrigins({ configured: [process.env.APP_URL], trustForwardedHeaders: true }),
58
+ trustedOrigins: trustedOrigins({
59
+ configured: [process.env.APP_URL],
60
+ trustForwardedHeaders: true,
61
+ }),
43
62
  })
44
63
  ```
45
64
 
46
- The application supplies its normal Better Auth configuration, including its database adapter, schema, plugins, callbacks, and product-specific policy.
47
-
48
- ## Server functions
49
-
50
- Framework access is injected, leaving TanStack Start available normally:
65
+ Framework access is injected into server helpers, so the package does not need to wrap TanStack Start:
51
66
 
52
67
  ```ts
53
68
  import { getRequest } from '@tanstack/react-start/server'
@@ -56,15 +71,19 @@ import { createRpc } from 'ras-stack/server'
56
71
 
57
72
  export const { rpc, mutationRpc } = createRpc({
58
73
  getRequest,
59
- requireMutation: (request) => requireSameOrigin(request, { configured: [process.env.APP_URL], trustForwardedHeaders: true }),
74
+ requireMutation: (request) =>
75
+ requireSameOrigin(request, {
76
+ configured: [process.env.APP_URL],
77
+ trustForwardedHeaders: true,
78
+ }),
60
79
  })
61
80
  ```
62
81
 
63
- Only enable `trustForwardedHeaders` when the application is deployed behind a proxy that replaces incoming forwarded headers.
82
+ Only enable `trustForwardedHeaders` behind a proxy that replaces incoming forwarded headers. Otherwise a client could choose the origin used by the check.
64
83
 
65
- ## Realtime
84
+ ## Realtime updates
66
85
 
67
- The realtime entrypoint owns Centrifugo transport mechanics and token signing. Applications own channel names, authorization, presence information, and publication payloads:
86
+ Applications choose their channel names, authorize subscriptions, and define payloads. `ras-stack` handles Centrifugo's HTTP publication and signed tokens:
68
87
 
69
88
  ```ts
70
89
  import { CentrifugoPublisher, signRealtimeToken } from 'ras-stack/realtime'
@@ -76,6 +95,7 @@ const publisher = new CentrifugoPublisher({
76
95
  maxPendingChannels: 1024,
77
96
  onError: (error, channel) => logger.error({ error, channel }, 'realtime publication failed'),
78
97
  })
98
+
79
99
  publisher.publish(`battle:${battle.id}`, { type: 'change' })
80
100
 
81
101
  const token = signRealtimeToken(user.id, { channel: `battle:${battle.id}`, info: presence }, { secret })
@@ -83,11 +103,11 @@ const token = signRealtimeToken(user.id, { channel: `battle:${battle.id}`, info:
83
103
  await publisher.close()
84
104
  ```
85
105
 
86
- `publish()` returns `false` when the publisher is closed, disabled, or at capacity. `close()` rejects new work and waits for accepted publications to finish, including the bounded retry budget.
106
+ `publish()` returns `false` when the publisher is closed, disabled, or at capacity. `close()` rejects new work and waits for accepted publications and their bounded retries to finish.
87
107
 
88
108
  ## Email and uploads
89
109
 
90
- Optional entrypoints integrate with dependencies that remain installed and available to the application:
110
+ The optional integrations return the underlying library objects when an application needs more control:
91
111
 
92
112
  ```ts
93
113
  import { createSmtpDelivery, createSmtpTransport, smtpConfigFromEnvironment } from 'ras-stack/email'
@@ -103,14 +123,15 @@ const upload = createTusUpload({
103
123
  shouldRetry: (status) => status !== 423,
104
124
  onProgress,
105
125
  })
126
+
106
127
  await startTusUpload(upload)
107
128
  ```
108
129
 
109
- `createSmtpTransport()` and `createTusUpload()` return the upstream objects, so applications can use capabilities the convenience wrappers do not cover. Applications retain ownership of email templates, missing-email behavior, upload metadata, authorization, quotas, and completion processing.
130
+ Applications retain ownership of email templates, missing-email behavior, upload metadata, authorization, quotas, and completion processing.
110
131
 
111
- ## Shared configuration
132
+ ## Shared project configuration
112
133
 
113
- Oxlint and TypeScript configurations are inheritable:
134
+ Extend the supplied configuration and override anything specific to the application:
114
135
 
115
136
  ```json
116
137
  {
@@ -128,21 +149,54 @@ Oxlint and TypeScript configurations are inheritable:
128
149
  }
129
150
  ```
130
151
 
152
+ TypeScript bases are also available at `ras-stack/config/typescript/browser` and `ras-stack/config/typescript/library`.
153
+
131
154
  ## GitHub Actions
132
155
 
133
- The shared setup action reads Node from `engines.node` and pnpm from `packageManager` in the consuming repository's `package.json`:
156
+ The JavaScript setup action reads the Node version from `engines.node` and the pnpm version from `packageManager` in the consuming repository:
134
157
 
135
158
  ```yaml
136
159
  steps:
137
160
  - uses: actions/checkout@v7
138
- - uses: richardsolomou/ras-stack/actions/setup-js@v0.1.0
139
- with:
140
- just-version: '1.58.0'
161
+ - uses: richardsolomou/ras-stack/actions/setup-js@v0.2.0
141
162
  - run: pnpm check
142
163
  ```
143
164
 
144
- Pin the action to an immutable release tag and let Dependabot propose upgrades.
165
+ Just is independent of the application language and is installed separately when a repository uses it:
166
+
167
+ ```yaml
168
+ - uses: richardsolomou/ras-stack/actions/setup-just@v0.2.0
169
+ with:
170
+ version: '1.58.0'
171
+ ```
172
+
173
+ Applications using Changesets can call the reusable release workflow after their own required checks:
174
+
175
+ ```yaml
176
+ release:
177
+ if: github.ref == 'refs/heads/main' && github.event_name == 'push'
178
+ needs: [check]
179
+ permissions:
180
+ contents: write
181
+ uses: richardsolomou/ras-stack/.github/workflows/release-changesets.yml@v0.2.0
182
+ secrets: inherit
183
+ ```
184
+
185
+ The workflow consumes pending changesets, commits the resulting versions and changelogs, pushes the commit and tag atomically, and creates a GitHub Release. It does nothing when no versioned changeset is present. The caller owns its checks, Changesets configuration, release policy, and any deployment that follows the release.
186
+
187
+ Pin actions and reusable workflows to a release tag and let Dependabot propose upgrades.
188
+
189
+ ## Development
190
+
191
+ Development requires Node 24 and pnpm 11.15.0.
192
+
193
+ ```sh
194
+ pnpm install
195
+ pnpm check
196
+ ```
197
+
198
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for release instructions. Report vulnerabilities privately as described in [SECURITY.md](SECURITY.md).
145
199
 
146
- ## License and security
200
+ ## License
147
201
 
148
- `ras-stack` is licensed under the GNU Affero General Public License v3.0. Report vulnerabilities through GitHub private vulnerability reporting as described in [SECURITY.md](SECURITY.md).
202
+ [GNU Affero General Public License v3.0](LICENSE).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ras-stack",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Composable full-stack primitives shared across Richard Solomou's applications.",
5
5
  "keywords": [
6
6
  "authentication",
@@ -14,7 +14,7 @@
14
14
  "license": "AGPL-3.0-only",
15
15
  "repository": {
16
16
  "type": "git",
17
- "url": "https://github.com/richardsolomou/ras-stack.git"
17
+ "url": "git+https://github.com/richardsolomou/ras-stack.git"
18
18
  },
19
19
  "files": [
20
20
  "dist",