@kaiz11/stack-client 0.0.14
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.
- package/LICENSE +32 -0
- package/README.md +586 -0
- package/dist/accounts/accounts-client.d.ts +188 -0
- package/dist/accounts/accounts-client.d.ts.map +1 -0
- package/dist/accounts/accounts-client.js +264 -0
- package/dist/accounts/accounts-client.js.map +1 -0
- package/dist/accounts/index.d.ts +8 -0
- package/dist/accounts/index.d.ts.map +1 -0
- package/dist/accounts/index.js +8 -0
- package/dist/accounts/index.js.map +1 -0
- package/dist/accounts/mock-accounts.d.ts +90 -0
- package/dist/accounts/mock-accounts.d.ts.map +1 -0
- package/dist/accounts/mock-accounts.js +434 -0
- package/dist/accounts/mock-accounts.js.map +1 -0
- package/dist/accounts/types.d.ts +180 -0
- package/dist/accounts/types.d.ts.map +1 -0
- package/dist/accounts/types.js +59 -0
- package/dist/accounts/types.js.map +1 -0
- package/dist/auth/auth-client.d.ts +224 -0
- package/dist/auth/auth-client.d.ts.map +1 -0
- package/dist/auth/auth-client.js +230 -0
- package/dist/auth/auth-client.js.map +1 -0
- package/dist/auth/base-auth.d.ts +44 -0
- package/dist/auth/base-auth.d.ts.map +1 -0
- package/dist/auth/base-auth.js +55 -0
- package/dist/auth/base-auth.js.map +1 -0
- package/dist/auth/index.d.ts +11 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +11 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/methods/admin.d.ts +59 -0
- package/dist/auth/methods/admin.d.ts.map +1 -0
- package/dist/auth/methods/admin.js +55 -0
- package/dist/auth/methods/admin.js.map +1 -0
- package/dist/auth/methods/index.d.ts +9 -0
- package/dist/auth/methods/index.d.ts.map +1 -0
- package/dist/auth/methods/index.js +8 -0
- package/dist/auth/methods/index.js.map +1 -0
- package/dist/auth/methods/magic-link.d.ts +27 -0
- package/dist/auth/methods/magic-link.d.ts.map +1 -0
- package/dist/auth/methods/magic-link.js +37 -0
- package/dist/auth/methods/magic-link.js.map +1 -0
- package/dist/auth/methods/mfa.d.ts +92 -0
- package/dist/auth/methods/mfa.d.ts.map +1 -0
- package/dist/auth/methods/mfa.js +153 -0
- package/dist/auth/methods/mfa.js.map +1 -0
- package/dist/auth/methods/oauth.d.ts +62 -0
- package/dist/auth/methods/oauth.d.ts.map +1 -0
- package/dist/auth/methods/oauth.js +165 -0
- package/dist/auth/methods/oauth.js.map +1 -0
- package/dist/auth/methods/otp.d.ts +43 -0
- package/dist/auth/methods/otp.d.ts.map +1 -0
- package/dist/auth/methods/otp.js +66 -0
- package/dist/auth/methods/otp.js.map +1 -0
- package/dist/auth/methods/password.d.ts +64 -0
- package/dist/auth/methods/password.d.ts.map +1 -0
- package/dist/auth/methods/password.js +116 -0
- package/dist/auth/methods/password.js.map +1 -0
- package/dist/auth/methods/recovery.d.ts +62 -0
- package/dist/auth/methods/recovery.d.ts.map +1 -0
- package/dist/auth/methods/recovery.js +100 -0
- package/dist/auth/methods/recovery.js.map +1 -0
- package/dist/auth/mock-auth.d.ts +135 -0
- package/dist/auth/mock-auth.d.ts.map +1 -0
- package/dist/auth/mock-auth.js +417 -0
- package/dist/auth/mock-auth.js.map +1 -0
- package/dist/auth/server/helpers.d.ts +215 -0
- package/dist/auth/server/helpers.d.ts.map +1 -0
- package/dist/auth/server/helpers.js +241 -0
- package/dist/auth/server/helpers.js.map +1 -0
- package/dist/auth/server/index.d.ts +24 -0
- package/dist/auth/server/index.d.ts.map +1 -0
- package/dist/auth/server/index.js +40 -0
- package/dist/auth/server/index.js.map +1 -0
- package/dist/auth/server/middleware.d.ts +305 -0
- package/dist/auth/server/middleware.d.ts.map +1 -0
- package/dist/auth/server/middleware.js +405 -0
- package/dist/auth/server/middleware.js.map +1 -0
- package/dist/auth/server/verify.d.ts +184 -0
- package/dist/auth/server/verify.d.ts.map +1 -0
- package/dist/auth/server/verify.js +222 -0
- package/dist/auth/server/verify.js.map +1 -0
- package/dist/auth/token-manager.d.ts +94 -0
- package/dist/auth/token-manager.d.ts.map +1 -0
- package/dist/auth/token-manager.js +231 -0
- package/dist/auth/token-manager.js.map +1 -0
- package/dist/auth/types.d.ts +412 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +66 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/auth/user/identities.d.ts +62 -0
- package/dist/auth/user/identities.d.ts.map +1 -0
- package/dist/auth/user/identities.js +88 -0
- package/dist/auth/user/identities.js.map +1 -0
- package/dist/auth/user/index.d.ts +4 -0
- package/dist/auth/user/index.d.ts.map +1 -0
- package/dist/auth/user/index.js +4 -0
- package/dist/auth/user/index.js.map +1 -0
- package/dist/auth/user/user.d.ts +64 -0
- package/dist/auth/user/user.d.ts.map +1 -0
- package/dist/auth/user/user.js +105 -0
- package/dist/auth/user/user.js.map +1 -0
- package/dist/auth/user/verification.d.ts +49 -0
- package/dist/auth/user/verification.d.ts.map +1 -0
- package/dist/auth/user/verification.js +71 -0
- package/dist/auth/user/verification.js.map +1 -0
- package/dist/cli/browser.d.ts +11 -0
- package/dist/cli/browser.d.ts.map +1 -0
- package/dist/cli/browser.js +35 -0
- package/dist/cli/browser.js.map +1 -0
- package/dist/cli/callback-server.d.ts +30 -0
- package/dist/cli/callback-server.d.ts.map +1 -0
- package/dist/cli/callback-server.js +100 -0
- package/dist/cli/callback-server.js.map +1 -0
- package/dist/cli/file-token-store.d.ts +79 -0
- package/dist/cli/file-token-store.d.ts.map +1 -0
- package/dist/cli/file-token-store.js +138 -0
- package/dist/cli/file-token-store.js.map +1 -0
- package/dist/cli/index.d.ts +33 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +38 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/oauth.d.ts +67 -0
- package/dist/cli/oauth.d.ts.map +1 -0
- package/dist/cli/oauth.js +101 -0
- package/dist/cli/oauth.js.map +1 -0
- package/dist/cli/pkce.d.ts +35 -0
- package/dist/cli/pkce.d.ts.map +1 -0
- package/dist/cli/pkce.js +43 -0
- package/dist/cli/pkce.js.map +1 -0
- package/dist/client.d.ts +22 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +99 -0
- package/dist/client.js.map +1 -0
- package/dist/db/client.d.ts +9 -0
- package/dist/db/client.d.ts.map +1 -0
- package/dist/db/client.js +19 -0
- package/dist/db/client.js.map +1 -0
- package/dist/db/errors.d.ts +19 -0
- package/dist/db/errors.d.ts.map +1 -0
- package/dist/db/errors.js +57 -0
- package/dist/db/errors.js.map +1 -0
- package/dist/db/index.d.ts +7 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +5 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/mock.d.ts +28 -0
- package/dist/db/mock.d.ts.map +1 -0
- package/dist/db/mock.js +459 -0
- package/dist/db/mock.js.map +1 -0
- package/dist/db/types.d.ts +73 -0
- package/dist/db/types.d.ts.map +1 -0
- package/dist/db/types.js +2 -0
- package/dist/db/types.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/errors.d.ts +33 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +76 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/http.d.ts +81 -0
- package/dist/lib/http.d.ts.map +1 -0
- package/dist/lib/http.js +163 -0
- package/dist/lib/http.js.map +1 -0
- package/dist/lib/keys.d.ts +87 -0
- package/dist/lib/keys.d.ts.map +1 -0
- package/dist/lib/keys.js +147 -0
- package/dist/lib/keys.js.map +1 -0
- package/dist/lib/paths.d.ts +37 -0
- package/dist/lib/paths.d.ts.map +1 -0
- package/dist/lib/paths.js +49 -0
- package/dist/lib/paths.js.map +1 -0
- package/dist/lib/token-store.d.ts +42 -0
- package/dist/lib/token-store.d.ts.map +1 -0
- package/dist/lib/token-store.js +75 -0
- package/dist/lib/token-store.js.map +1 -0
- package/dist/mocks/handlers.d.ts +29 -0
- package/dist/mocks/handlers.d.ts.map +1 -0
- package/dist/mocks/handlers.js +79 -0
- package/dist/mocks/handlers.js.map +1 -0
- package/dist/mocks/index.d.ts +5 -0
- package/dist/mocks/index.d.ts.map +1 -0
- package/dist/mocks/index.js +9 -0
- package/dist/mocks/index.js.map +1 -0
- package/dist/mocks/responses.d.ts +76 -0
- package/dist/mocks/responses.d.ts.map +1 -0
- package/dist/mocks/responses.js +91 -0
- package/dist/mocks/responses.js.map +1 -0
- package/dist/mocks/server.d.ts +7 -0
- package/dist/mocks/server.d.ts.map +1 -0
- package/dist/mocks/server.js +9 -0
- package/dist/mocks/server.js.map +1 -0
- package/dist/mocks/state.d.ts +86 -0
- package/dist/mocks/state.d.ts.map +1 -0
- package/dist/mocks/state.js +77 -0
- package/dist/mocks/state.js.map +1 -0
- package/dist/storage/bucket-ref.d.ts +183 -0
- package/dist/storage/bucket-ref.d.ts.map +1 -0
- package/dist/storage/bucket-ref.js +529 -0
- package/dist/storage/bucket-ref.js.map +1 -0
- package/dist/storage/errors.d.ts +27 -0
- package/dist/storage/errors.d.ts.map +1 -0
- package/dist/storage/errors.js +89 -0
- package/dist/storage/errors.js.map +1 -0
- package/dist/storage/index.d.ts +13 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +11 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/interface.d.ts +245 -0
- package/dist/storage/interface.d.ts.map +1 -0
- package/dist/storage/interface.js +2 -0
- package/dist/storage/interface.js.map +1 -0
- package/dist/storage/mock-storage.d.ts +67 -0
- package/dist/storage/mock-storage.d.ts.map +1 -0
- package/dist/storage/mock-storage.js +478 -0
- package/dist/storage/mock-storage.js.map +1 -0
- package/dist/storage/policies-client.d.ts +77 -0
- package/dist/storage/policies-client.d.ts.map +1 -0
- package/dist/storage/policies-client.js +115 -0
- package/dist/storage/policies-client.js.map +1 -0
- package/dist/storage/policy-templates.d.ts +6 -0
- package/dist/storage/policy-templates.d.ts.map +1 -0
- package/dist/storage/policy-templates.js +290 -0
- package/dist/storage/policy-templates.js.map +1 -0
- package/dist/storage/policy-types.d.ts +98 -0
- package/dist/storage/policy-types.d.ts.map +1 -0
- package/dist/storage/policy-types.js +20 -0
- package/dist/storage/policy-types.js.map +1 -0
- package/dist/storage/storage-client.d.ts +32 -0
- package/dist/storage/storage-client.d.ts.map +1 -0
- package/dist/storage/storage-client.js +94 -0
- package/dist/storage/storage-client.js.map +1 -0
- package/dist/storage/tus-upload.d.ts +56 -0
- package/dist/storage/tus-upload.d.ts.map +1 -0
- package/dist/storage/tus-upload.js +236 -0
- package/dist/storage/tus-upload.js.map +1 -0
- package/dist/storage/types.d.ts +335 -0
- package/dist/storage/types.d.ts.map +1 -0
- package/dist/storage/types.js +39 -0
- package/dist/storage/types.js.map +1 -0
- package/dist/test/auth/helpers.d.ts +33 -0
- package/dist/test/auth/helpers.d.ts.map +1 -0
- package/dist/test/auth/helpers.js +80 -0
- package/dist/test/auth/helpers.js.map +1 -0
- package/dist/test/helpers/jwt.d.ts +61 -0
- package/dist/test/helpers/jwt.d.ts.map +1 -0
- package/dist/test/helpers/jwt.js +132 -0
- package/dist/test/helpers/jwt.js.map +1 -0
- package/dist/test/helpers/mailpit.d.ts +61 -0
- package/dist/test/helpers/mailpit.d.ts.map +1 -0
- package/dist/test/helpers/mailpit.js +107 -0
- package/dist/test/helpers/mailpit.js.map +1 -0
- package/dist/test/setup.d.ts +2 -0
- package/dist/test/setup.d.ts.map +1 -0
- package/dist/test/setup.js +17 -0
- package/dist/test/setup.js.map +1 -0
- package/dist/types.d.ts +96 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +78 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
Copyright (c) 2026 Kai Zhao. All rights reserved.
|
|
2
|
+
|
|
3
|
+
PROPRIETARY SOFTWARE LICENSE
|
|
4
|
+
|
|
5
|
+
This software and associated documentation files (the "Software") are the
|
|
6
|
+
proprietary and confidential property of Kai Zhao.
|
|
7
|
+
|
|
8
|
+
RESTRICTIONS
|
|
9
|
+
|
|
10
|
+
You may NOT, without prior written permission from the copyright holder:
|
|
11
|
+
|
|
12
|
+
1. Copy, reproduce, or duplicate the Software or any portion thereof
|
|
13
|
+
2. Modify, adapt, translate, or create derivative works based on the Software
|
|
14
|
+
3. Distribute, sublicense, lease, rent, loan, or otherwise transfer the Software
|
|
15
|
+
4. Reverse engineer, disassemble, decompile, or attempt to derive the source
|
|
16
|
+
code of the Software
|
|
17
|
+
5. Remove or alter any proprietary notices, labels, or marks on the Software
|
|
18
|
+
6. Use the Software for any commercial purpose
|
|
19
|
+
7. Make the Software available to any third party
|
|
20
|
+
|
|
21
|
+
DISCLAIMER
|
|
22
|
+
|
|
23
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
24
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
25
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
26
|
+
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
27
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
28
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
29
|
+
|
|
30
|
+
CONTACT
|
|
31
|
+
|
|
32
|
+
For licensing inquiries, please contact the copyright holder.
|
package/README.md
ADDED
|
@@ -0,0 +1,586 @@
|
|
|
1
|
+
# @kaiz11/stack-client
|
|
2
|
+
|
|
3
|
+
A standalone TypeScript client for Supabase stack services. Supports both platform-level and tenant-level operations with automatic token management.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
This package is published to GitHub Packages. Configure your `.npmrc` to use the GitHub registry for the `@kaiz11` scope:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# .npmrc (project root or ~/.npmrc)
|
|
11
|
+
@kaiz11:registry=https://npm.pkg.github.com
|
|
12
|
+
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Then install:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pnpm add @kaiz11/stack-client
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Authentication
|
|
22
|
+
|
|
23
|
+
GitHub Packages requires authentication even for public packages. Create a [Personal Access Token](https://github.com/settings/tokens) with the `read:packages` scope and set it as `GITHUB_TOKEN`:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Option 1: Environment variable
|
|
27
|
+
export GITHUB_TOKEN=ghp_xxxxxxxxxxxx
|
|
28
|
+
|
|
29
|
+
# Option 2: npm login
|
|
30
|
+
npm login --registry=https://npm.pkg.github.com
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
For CI/CD, use the built-in `GITHUB_TOKEN` secret or a PAT with `read:packages` scope.
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import { createTenantClient } from "@kaiz11/stack-client";
|
|
39
|
+
|
|
40
|
+
const client = createTenantClient({
|
|
41
|
+
baseUrl: "https://stack.example.com",
|
|
42
|
+
tenantId: "my-tenant",
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Sign in
|
|
46
|
+
await client.auth.signInWithPassword({
|
|
47
|
+
email: "user@example.com",
|
|
48
|
+
password: "password123",
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Access the user
|
|
52
|
+
const user = client.auth.getUser();
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Client Modes
|
|
56
|
+
|
|
57
|
+
### Tenant Mode
|
|
58
|
+
|
|
59
|
+
For applications serving a single tenant. Endpoints use `/auth/{tenantId}/*`.
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import { createTenantClient } from "@kaiz11/stack-client";
|
|
63
|
+
|
|
64
|
+
const client = createTenantClient({
|
|
65
|
+
baseUrl: "https://stack.example.com",
|
|
66
|
+
tenantId: "acme-corp",
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Platform Mode
|
|
71
|
+
|
|
72
|
+
For platform-level operations (admin dashboards, multi-tenant management). Endpoints use `/auth/_platform/*`.
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import { createPlatformClient } from "@kaiz11/stack-client";
|
|
76
|
+
|
|
77
|
+
const client = createPlatformClient({
|
|
78
|
+
baseUrl: "https://stack.example.com",
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Generic Mode
|
|
83
|
+
|
|
84
|
+
When you need to specify the mode dynamically, use `createClient()` with `tenantId` for tenant mode, or without it for platform mode:
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
import { createClient } from "@kaiz11/stack-client";
|
|
88
|
+
|
|
89
|
+
// Tenant mode (tenantId present)
|
|
90
|
+
const tenantClient = createClient({
|
|
91
|
+
baseUrl: "https://stack.example.com",
|
|
92
|
+
tenantId: "acme-corp",
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Platform mode (no tenantId)
|
|
96
|
+
const platformClient = createClient({
|
|
97
|
+
baseUrl: "https://stack.example.com",
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Configuration
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
interface ClientConfig {
|
|
105
|
+
/** Base URL of the stack server */
|
|
106
|
+
baseUrl: string;
|
|
107
|
+
|
|
108
|
+
/** Token storage type: "memory" | "localStorage" | custom TokenStore */
|
|
109
|
+
tokenStore?: TokenStoreType;
|
|
110
|
+
|
|
111
|
+
/** Storage key prefix (default: "stack") */
|
|
112
|
+
storagePrefix?: string;
|
|
113
|
+
|
|
114
|
+
/** Request timeout in milliseconds (default: 30000) */
|
|
115
|
+
timeout?: number;
|
|
116
|
+
|
|
117
|
+
/** Enable mock mode for testing (no HTTP requests) */
|
|
118
|
+
mock?: boolean;
|
|
119
|
+
|
|
120
|
+
/** Mock options (latency simulation, etc.) */
|
|
121
|
+
mockOptions?: MockOptions;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
interface TenantClientConfig extends ClientConfig {
|
|
125
|
+
/** Tenant identifier */
|
|
126
|
+
tenantId: string;
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Token Storage
|
|
131
|
+
|
|
132
|
+
Tokens need to be stored somewhere. The client supports pluggable storage backends:
|
|
133
|
+
|
|
134
|
+
### Memory Store (Default)
|
|
135
|
+
|
|
136
|
+
Tokens are stored in memory. Lost on page refresh. Good for server-side usage.
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
import { createTenantClient, MemoryTokenStore } from "@kaiz11/stack-client";
|
|
140
|
+
|
|
141
|
+
const client = createTenantClient({
|
|
142
|
+
baseUrl: "https://stack.example.com",
|
|
143
|
+
tenantId: "my-tenant",
|
|
144
|
+
tokenStore: new MemoryTokenStore(), // This is the default
|
|
145
|
+
});
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### LocalStorage Store
|
|
149
|
+
|
|
150
|
+
Tokens persist in browser localStorage. Survives page refreshes.
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
import {
|
|
154
|
+
createTenantClient,
|
|
155
|
+
LocalStorageTokenStore,
|
|
156
|
+
} from "@kaiz11/stack-client";
|
|
157
|
+
|
|
158
|
+
const client = createTenantClient({
|
|
159
|
+
baseUrl: "https://stack.example.com",
|
|
160
|
+
tenantId: "my-tenant",
|
|
161
|
+
tokenStore: new LocalStorageTokenStore("my-app-auth"),
|
|
162
|
+
});
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Custom Store
|
|
166
|
+
|
|
167
|
+
Implement the `TokenStore` interface for custom backends (cookies, IndexedDB, etc.):
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
import { TokenStore } from "@kaiz11/stack-client";
|
|
171
|
+
|
|
172
|
+
class CookieTokenStore implements TokenStore {
|
|
173
|
+
getAccessToken(): string | null {
|
|
174
|
+
return getCookie("access_token");
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
getRefreshToken(): string | null {
|
|
178
|
+
return getCookie("refresh_token");
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
setTokens(accessToken: string, refreshToken: string): void {
|
|
182
|
+
setCookie("access_token", accessToken, { httpOnly: true, secure: true });
|
|
183
|
+
setCookie("refresh_token", refreshToken, { httpOnly: true, secure: true });
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
clearTokens(): void {
|
|
187
|
+
deleteCookie("access_token");
|
|
188
|
+
deleteCookie("refresh_token");
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Cleanup
|
|
194
|
+
|
|
195
|
+
When done with the client, call `destroy()` to clear auto-refresh timers:
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
const client = createTenantClient({ ... });
|
|
199
|
+
|
|
200
|
+
// When cleaning up (e.g., component unmount)
|
|
201
|
+
client.destroy();
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Server-Side Usage
|
|
205
|
+
|
|
206
|
+
The same client works on both frontend and backend. For server-side usage, use `tokenStore: "memory"` and pass `accessToken` directly.
|
|
207
|
+
|
|
208
|
+
> **Note:** The client does not verify tokens passed via `accessToken`. It assumes the token is valid and uses it for outgoing API calls. Verification happens server-side when the request reaches PostgREST, Storage, or other backend services. If you need to verify incoming requests, use the [Auth Middleware](#auth-middleware-verifying-incoming-requests).
|
|
209
|
+
|
|
210
|
+
### Service Role (Admin Access)
|
|
211
|
+
|
|
212
|
+
Use a service role token for admin operations that bypass RLS:
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
import { createTenantClient } from "@kaiz11/stack-client";
|
|
216
|
+
|
|
217
|
+
const adminClient = createTenantClient({
|
|
218
|
+
baseUrl: "https://stack.example.com",
|
|
219
|
+
tenantId: "acme-corp",
|
|
220
|
+
tokenStore: "memory",
|
|
221
|
+
accessToken: process.env.SERVICE_ROLE_TOKEN,
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// Admin operations - bypasses RLS
|
|
225
|
+
await adminClient.storage.from("uploads").upload("file.pdf", data);
|
|
226
|
+
await adminClient.storage.from("private").list(); // Can access all files
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### User Context (Respects RLS)
|
|
230
|
+
|
|
231
|
+
Pass the user's access token (from a verified request) to make calls scoped to that user:
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
import { createTenantClient } from "@kaiz11/stack-client";
|
|
235
|
+
|
|
236
|
+
// In your API handler, after verifying the request
|
|
237
|
+
const userClient = createTenantClient({
|
|
238
|
+
baseUrl: "https://stack.example.com",
|
|
239
|
+
tenantId: "acme-corp",
|
|
240
|
+
tokenStore: "memory",
|
|
241
|
+
accessToken: userAccessToken, // From verified JWT
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// User-scoped operations - respects RLS
|
|
245
|
+
await userClient.storage.from("user-files").list(); // Only sees their files
|
|
246
|
+
await userClient.accounts.list(); // Only sees their accounts
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Auth Middleware (Verifying Incoming Requests)
|
|
250
|
+
|
|
251
|
+
Use the auth middleware to verify JWTs on incoming requests. The middleware automatically fetches the JWKS from GoTrue for ES256 verification.
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
import { Hono } from "hono";
|
|
255
|
+
import { createStackAuthMiddleware } from "@kaiz11/stack-client/auth/server";
|
|
256
|
+
|
|
257
|
+
const app = new Hono();
|
|
258
|
+
|
|
259
|
+
// For tenant APIs
|
|
260
|
+
app.use(
|
|
261
|
+
"*",
|
|
262
|
+
createStackAuthMiddleware({
|
|
263
|
+
baseUrl: "https://stack.example.com",
|
|
264
|
+
tenantId: "acme-corp",
|
|
265
|
+
excludePaths: [/^\/health$/], // Optional: skip auth for these paths
|
|
266
|
+
}),
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
// For platform APIs
|
|
270
|
+
app.use(
|
|
271
|
+
"*",
|
|
272
|
+
createStackAuthMiddleware({
|
|
273
|
+
baseUrl: "https://stack.example.com",
|
|
274
|
+
// No tenantId = platform mode
|
|
275
|
+
}),
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
// Access verified user in handlers
|
|
279
|
+
app.get("/api/me", (c) => {
|
|
280
|
+
const user = c.get("user"); // { id, email, role, aal, ... }
|
|
281
|
+
const token = c.get("accessToken"); // Raw JWT for downstream calls
|
|
282
|
+
return c.json({ user });
|
|
283
|
+
});
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Optional Auth Middleware
|
|
287
|
+
|
|
288
|
+
For routes that work with or without authentication:
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
import { optionalStackAuthMiddleware } from "@kaiz11/stack-client/auth/server";
|
|
292
|
+
|
|
293
|
+
app.use(
|
|
294
|
+
"*",
|
|
295
|
+
optionalStackAuthMiddleware({
|
|
296
|
+
baseUrl: "https://stack.example.com",
|
|
297
|
+
tenantId: "acme-corp",
|
|
298
|
+
}),
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
app.get("/api/feed", (c) => {
|
|
302
|
+
const user = c.get("user"); // undefined if not authenticated
|
|
303
|
+
if (user) {
|
|
304
|
+
return c.json({ feed: getPersonalizedFeed(user.id) });
|
|
305
|
+
}
|
|
306
|
+
return c.json({ feed: getPublicFeed() });
|
|
307
|
+
});
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Role & MFA Guards
|
|
311
|
+
|
|
312
|
+
Add additional guards after the auth middleware:
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
import {
|
|
316
|
+
createStackAuthMiddleware,
|
|
317
|
+
requireRoleMiddleware,
|
|
318
|
+
requireMfaMiddleware,
|
|
319
|
+
} from "@kaiz11/stack-client/auth/server";
|
|
320
|
+
|
|
321
|
+
// All routes require authentication
|
|
322
|
+
app.use("*", createStackAuthMiddleware({ baseUrl, tenantId }));
|
|
323
|
+
|
|
324
|
+
// Admin routes require service_role
|
|
325
|
+
app.use("/admin/*", requireRoleMiddleware("service_role"));
|
|
326
|
+
|
|
327
|
+
// Sensitive routes require MFA (AAL2)
|
|
328
|
+
app.use("/settings/security/*", requireMfaMiddleware());
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Complete Server Example
|
|
332
|
+
|
|
333
|
+
```typescript
|
|
334
|
+
import { Hono } from "hono";
|
|
335
|
+
import { createTenantClient } from "@kaiz11/stack-client";
|
|
336
|
+
import { createStackAuthMiddleware } from "@kaiz11/stack-client/auth/server";
|
|
337
|
+
|
|
338
|
+
const app = new Hono();
|
|
339
|
+
|
|
340
|
+
const baseUrl = "https://stack.example.com";
|
|
341
|
+
const tenantId = "acme-corp";
|
|
342
|
+
|
|
343
|
+
// Verify incoming requests
|
|
344
|
+
app.use("/api/*", createStackAuthMiddleware({ baseUrl, tenantId }));
|
|
345
|
+
|
|
346
|
+
// User uploads a file (respects RLS)
|
|
347
|
+
app.post("/api/upload", async (c) => {
|
|
348
|
+
const userClient = createTenantClient({
|
|
349
|
+
baseUrl,
|
|
350
|
+
tenantId,
|
|
351
|
+
tokenStore: "memory",
|
|
352
|
+
accessToken: c.get("accessToken"),
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
const file = await c.req.blob();
|
|
356
|
+
const { data, error } = await userClient.storage
|
|
357
|
+
.from("uploads")
|
|
358
|
+
.upload(`${c.get("user").id}/file.pdf`, file);
|
|
359
|
+
|
|
360
|
+
return c.json({ data, error });
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
// Admin generates a report (bypasses RLS)
|
|
364
|
+
app.post("/api/admin/report", async (c) => {
|
|
365
|
+
const adminClient = createTenantClient({
|
|
366
|
+
baseUrl,
|
|
367
|
+
tenantId,
|
|
368
|
+
tokenStore: "memory",
|
|
369
|
+
accessToken: process.env.SERVICE_ROLE_TOKEN,
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
// Can access all users' files
|
|
373
|
+
const { data } = await adminClient.storage.from("uploads").list();
|
|
374
|
+
return c.json({ files: data });
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
export default app;
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
## Testing
|
|
381
|
+
|
|
382
|
+
The client includes built-in mock mode for testing without HTTP requests.
|
|
383
|
+
|
|
384
|
+
### Mock Mode
|
|
385
|
+
|
|
386
|
+
Enable mock mode for unit tests or local development:
|
|
387
|
+
|
|
388
|
+
```typescript
|
|
389
|
+
const client = createTenantClient({
|
|
390
|
+
baseUrl: "https://stack.example.com",
|
|
391
|
+
tenantId: "test-tenant",
|
|
392
|
+
mock: true,
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
// All auth methods work without network requests
|
|
396
|
+
await client.auth.signIn({ email: "test@example.com", password: "password" });
|
|
397
|
+
const user = client.auth.getUser(); // Returns mock user
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
### Simulating Latency
|
|
401
|
+
|
|
402
|
+
Add artificial delay to simulate network conditions:
|
|
403
|
+
|
|
404
|
+
```typescript
|
|
405
|
+
const client = createTenantClient({
|
|
406
|
+
baseUrl: "https://stack.example.com",
|
|
407
|
+
tenantId: "test-tenant",
|
|
408
|
+
mock: true,
|
|
409
|
+
mockOptions: {
|
|
410
|
+
latency: 100, // 100ms delay on all operations
|
|
411
|
+
},
|
|
412
|
+
});
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
### Controlling Mock Behavior
|
|
416
|
+
|
|
417
|
+
Use `mockState` to simulate errors and edge cases:
|
|
418
|
+
|
|
419
|
+
```typescript
|
|
420
|
+
import { createTenantClient, mockState } from "@kaiz11/stack-client";
|
|
421
|
+
|
|
422
|
+
const client = createTenantClient({ ..., mock: true });
|
|
423
|
+
|
|
424
|
+
// Simulate sign-in failure
|
|
425
|
+
mockState.shouldFailSignIn = true;
|
|
426
|
+
await client.auth.signIn({ email, password }); // Throws AuthError
|
|
427
|
+
|
|
428
|
+
// Simulate sign-up failure (user exists)
|
|
429
|
+
mockState.shouldFailSignUp = true;
|
|
430
|
+
await client.auth.signUp({ email, password }); // Throws AuthError
|
|
431
|
+
|
|
432
|
+
// Simulate token refresh failure
|
|
433
|
+
mockState.shouldFailRefresh = true;
|
|
434
|
+
await client.auth.refreshSession(); // Throws AuthError
|
|
435
|
+
|
|
436
|
+
// Reset to default behavior
|
|
437
|
+
mockState.reset();
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
### Tracking Request Counts
|
|
441
|
+
|
|
442
|
+
Use `requestCounts` to verify operations were called:
|
|
443
|
+
|
|
444
|
+
```typescript
|
|
445
|
+
import { createTenantClient, requestCounts } from "@kaiz11/stack-client";
|
|
446
|
+
|
|
447
|
+
const client = createTenantClient({ ..., mock: true });
|
|
448
|
+
|
|
449
|
+
await client.auth.signIn({ email, password });
|
|
450
|
+
console.log(requestCounts.signIn); // 1
|
|
451
|
+
|
|
452
|
+
await client.auth.signOut();
|
|
453
|
+
console.log(requestCounts.signOut); // 1
|
|
454
|
+
|
|
455
|
+
// Reset counts between tests
|
|
456
|
+
requestCounts.reset();
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### Vitest Example
|
|
460
|
+
|
|
461
|
+
```typescript
|
|
462
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
463
|
+
import {
|
|
464
|
+
createTenantClient,
|
|
465
|
+
mockState,
|
|
466
|
+
requestCounts,
|
|
467
|
+
} from "@kaiz11/stack-client";
|
|
468
|
+
|
|
469
|
+
describe("auth", () => {
|
|
470
|
+
let client: ReturnType<typeof createTenantClient>;
|
|
471
|
+
|
|
472
|
+
beforeEach(() => {
|
|
473
|
+
client = createTenantClient({
|
|
474
|
+
baseUrl: "https://stack.example.com",
|
|
475
|
+
tenantId: "test",
|
|
476
|
+
mock: true,
|
|
477
|
+
});
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
afterEach(() => {
|
|
481
|
+
client.destroy();
|
|
482
|
+
mockState.reset();
|
|
483
|
+
requestCounts.reset();
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
it("signs in user", async () => {
|
|
487
|
+
await client.auth.signIn({ email: "test@example.com", password: "pass" });
|
|
488
|
+
|
|
489
|
+
expect(client.auth.isAuthenticated()).toBe(true);
|
|
490
|
+
expect(requestCounts.signIn).toBe(1);
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
it("handles sign-in error", async () => {
|
|
494
|
+
mockState.shouldFailSignIn = true;
|
|
495
|
+
|
|
496
|
+
await expect(
|
|
497
|
+
client.auth.signIn({ email: "test@example.com", password: "wrong" }),
|
|
498
|
+
).rejects.toThrow();
|
|
499
|
+
});
|
|
500
|
+
});
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
## Known Limitations
|
|
504
|
+
|
|
505
|
+
Some client features require backend configuration or are not yet supported by the self-hosted Stack platform.
|
|
506
|
+
|
|
507
|
+
### Features Requiring Backend Configuration
|
|
508
|
+
|
|
509
|
+
| Feature | Requirement | Status |
|
|
510
|
+
| --------------------------- | --------------------------------------------- | ---------------------------- |
|
|
511
|
+
| **Phone/SMS Auth** | Twilio configuration in GoTrue | Not configured |
|
|
512
|
+
| **Manual Identity Linking** | `GOTRUE_SECURITY_MANUAL_LINKING_ENABLED=true` | Disabled by default |
|
|
513
|
+
| **TUS Resumable Uploads** | Storage service TUS endpoint | Returns 500 (platform issue) |
|
|
514
|
+
|
|
515
|
+
### Features Requiring Browser Interaction
|
|
516
|
+
|
|
517
|
+
These features work correctly but cannot be fully automated in tests:
|
|
518
|
+
|
|
519
|
+
| Feature | Reason |
|
|
520
|
+
| -------------------------- | ------------------------------------- |
|
|
521
|
+
| **OAuth Sign-In** | Requires browser redirect to provider |
|
|
522
|
+
| **OAuth Identity Linking** | Requires browser OAuth flow |
|
|
523
|
+
| **PKCE OAuth Flow** | Requires browser for code exchange |
|
|
524
|
+
|
|
525
|
+
### API Differences from Official Supabase
|
|
526
|
+
|
|
527
|
+
| Method | Difference |
|
|
528
|
+
| ------------------------ | ------------------------------------------------- |
|
|
529
|
+
| `auth.mfa.listFactors()` | Returns 405 (endpoint not available in GoTrue) |
|
|
530
|
+
| `auth.reauthenticate()` | Sends OTP email, returns `void` (not `{ nonce }`) |
|
|
531
|
+
|
|
532
|
+
### Workarounds
|
|
533
|
+
|
|
534
|
+
**For `reauthenticate()`**: The OTP sent to email should be used as the `nonce` parameter in `updatePassword()`:
|
|
535
|
+
|
|
536
|
+
```typescript
|
|
537
|
+
// Request reauthentication (sends OTP to email)
|
|
538
|
+
await client.auth.reauthenticate();
|
|
539
|
+
|
|
540
|
+
// User receives OTP via email, enters it
|
|
541
|
+
const otp = "123456"; // from email
|
|
542
|
+
|
|
543
|
+
// Use OTP as nonce for password update
|
|
544
|
+
await client.auth.updatePassword({
|
|
545
|
+
password: "new-password",
|
|
546
|
+
nonce: otp,
|
|
547
|
+
});
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
## Modules
|
|
551
|
+
|
|
552
|
+
| Module | Description | Documentation |
|
|
553
|
+
| ----------- | --------------------------------------------------- | ------------------------------------------- |
|
|
554
|
+
| `auth` | Authentication (sign in, sign up, sign out, tokens) | [Auth README](./src/auth/README.md) |
|
|
555
|
+
| `accounts` | Multi-tenant accounts (teams, roles, members) | [Accounts README](./src/accounts/README.md) |
|
|
556
|
+
| `storage` | File storage (buckets, uploads, signed URLs, TUS) | [Storage README](./src/storage/README.md) |
|
|
557
|
+
| `functions` | Edge functions | Coming soon |
|
|
558
|
+
| `realtime` | Realtime subscriptions | Coming soon |
|
|
559
|
+
|
|
560
|
+
## TypeScript Types
|
|
561
|
+
|
|
562
|
+
```typescript
|
|
563
|
+
import type {
|
|
564
|
+
// Client
|
|
565
|
+
StackClient,
|
|
566
|
+
ClientConfig,
|
|
567
|
+
TenantClientConfig,
|
|
568
|
+
PlatformClientConfig,
|
|
569
|
+
ClientMode,
|
|
570
|
+
|
|
571
|
+
// Token storage
|
|
572
|
+
TokenStore,
|
|
573
|
+
TokenStoreType,
|
|
574
|
+
|
|
575
|
+
// Mock mode
|
|
576
|
+
MockOptions,
|
|
577
|
+
MockState,
|
|
578
|
+
RequestCounts,
|
|
579
|
+
} from "@kaiz11/stack-client";
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
## License
|
|
583
|
+
|
|
584
|
+
Copyright © 2026 Kai Zhao. All rights reserved.
|
|
585
|
+
|
|
586
|
+
This software is proprietary and confidential. Unauthorized copying, distribution, modification, or use of this software, via any medium, is strictly prohibited without prior written permission from the copyright holder.
|